@terpjs/spec 0.14.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 (392) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +417 -0
  3. package/VERSION +1 -0
  4. package/app-check-report.schema.json +122 -0
  5. package/assurance-profile.schema.json +70 -0
  6. package/catalog/backend/alembic_downgrades_not_empty.json +22 -0
  7. package/catalog/backend/base_query_not_overridden.json +27 -0
  8. package/catalog/backend/canonical_module_shape.json +22 -0
  9. package/catalog/backend/datetime_columns_are_timezone_aware.json +23 -0
  10. package/catalog/backend/escape_hatch_budget.json +21 -0
  11. package/catalog/backend/events_reference_catalog.json +27 -0
  12. package/catalog/backend/input_schemas_exclude_managed_columns.json +27 -0
  13. package/catalog/backend/input_str_fields_have_max_length.json +22 -0
  14. package/catalog/backend/jobs_reference_catalog.json +27 -0
  15. package/catalog/backend/list_routes_paginate.json +32 -0
  16. package/catalog/backend/modules_declare_policy.json +26 -0
  17. package/catalog/backend/mutations_emit_audit.json +26 -0
  18. package/catalog/backend/mutations_require_write_role.json +32 -0
  19. package/catalog/backend/no_adhoc_background_runtime.json +22 -0
  20. package/catalog/backend/no_adhoc_config_decrypt.json +27 -0
  21. package/catalog/backend/no_adhoc_logging_config.json +22 -0
  22. package/catalog/backend/no_adhoc_middleware.json +27 -0
  23. package/catalog/backend/no_adhoc_permission_literals.json +27 -0
  24. package/catalog/backend/no_app_instantiation.json +22 -0
  25. package/catalog/backend/no_blocking_sleep.json +22 -0
  26. package/catalog/backend/no_cross_module_imports.json +21 -0
  27. package/catalog/backend/no_dependency_overrides.json +27 -0
  28. package/catalog/backend/no_destructive_migrations.json +22 -0
  29. package/catalog/backend/no_dynamic_sql.json +22 -0
  30. package/catalog/backend/no_empty_tests.json +22 -0
  31. package/catalog/backend/no_eval_or_exec.json +22 -0
  32. package/catalog/backend/no_hardcoded_credentials.json +21 -0
  33. package/catalog/backend/no_internal_imports.json +22 -0
  34. package/catalog/backend/no_manual_actor_stamping.json +27 -0
  35. package/catalog/backend/no_manual_ownership_checks.json +32 -0
  36. package/catalog/backend/no_manual_scope_filtering.json +27 -0
  37. package/catalog/backend/no_manual_table_schema.json +22 -0
  38. package/catalog/backend/no_manual_version_assignment.json +22 -0
  39. package/catalog/backend/no_mutable_default_args.json +22 -0
  40. package/catalog/backend/no_naive_datetime.json +22 -0
  41. package/catalog/backend/no_oversized_python_files.json +22 -0
  42. package/catalog/backend/no_print.json +22 -0
  43. package/catalog/backend/no_raw_app_routes.json +26 -0
  44. package/catalog/backend/no_raw_connection_access.json +27 -0
  45. package/catalog/backend/no_raw_file_references.json +27 -0
  46. package/catalog/backend/no_raw_outbound_http.json +22 -0
  47. package/catalog/backend/no_raw_session_construction.json +22 -0
  48. package/catalog/backend/no_star_imports.json +22 -0
  49. package/catalog/backend/no_todo_fixme.json +22 -0
  50. package/catalog/backend/no_unique_columns_on_soft_delete_models.json +22 -0
  51. package/catalog/backend/offset_queries_declare_ordering.json +22 -0
  52. package/catalog/backend/path_id_params_are_uuid.json +22 -0
  53. package/catalog/backend/policy_refs_resolve.json +27 -0
  54. package/catalog/backend/public_modules_are_read_only.json +26 -0
  55. package/catalog/backend/reads_use_base_query.json +27 -0
  56. package/catalog/backend/response_model_not_table_model.json +27 -0
  57. package/catalog/backend/routes_declare_response_model.json +27 -0
  58. package/catalog/backend/safe_methods_are_read_only.json +31 -0
  59. package/catalog/backend/schemas_exclude_sensitive_fields.json +32 -0
  60. package/catalog/backend/session_imported_from_sqlmodel.json +22 -0
  61. package/catalog/backend/table_models_use_base_table.json +22 -0
  62. package/catalog/backend/tables_have_migrations.json +27 -0
  63. package/catalog/backend/tenant_scoped_models_use_scoped_service.json +22 -0
  64. package/catalog/backend/ungoverned_escape_hatch.json +21 -0
  65. package/catalog/backend/update_schemas_inherit_base_update_schema.json +32 -0
  66. package/catalog/frontend/escape-hatch.json +21 -0
  67. package/catalog/frontend/generated-client-only.json +26 -0
  68. package/catalog/frontend/layout-contract.json +26 -0
  69. package/catalog/frontend/no-cross-module-imports.json +21 -0
  70. package/catalog/frontend/no-deep-imports.json +25 -0
  71. package/catalog/frontend/no-dom-html-injection.json +22 -0
  72. package/catalog/frontend/no-eval.json +21 -0
  73. package/catalog/frontend/no-inline-styling.json +25 -0
  74. package/catalog/frontend/no-style-imports.json +24 -0
  75. package/catalog/frontend/no-unsafe-href.json +21 -0
  76. package/catalog/frontend/no-unsafe-target-blank.json +21 -0
  77. package/catalog/frontend/router-links.json +22 -0
  78. package/catalog/frontend/token-styled-elements.json +25 -0
  79. package/catalog/schema.json +95 -0
  80. package/corpus/PENDING.json +4 -0
  81. package/corpus/RESIDUALS.json +21 -0
  82. package/corpus/backend/alembic_downgrades_not_empty/compliant-01/modules/notes/migrations/versions/0001_change.py +6 -0
  83. package/corpus/backend/alembic_downgrades_not_empty/violation-01/modules/notes/migrations/versions/0001_change.py +6 -0
  84. package/corpus/backend/base_query_not_overridden/compliant-01/modules/notes/service.py +8 -0
  85. package/corpus/backend/base_query_not_overridden/compliant-02/modules/notes/service.py +8 -0
  86. package/corpus/backend/base_query_not_overridden/violation-01/modules/notes/service.py +8 -0
  87. package/corpus/backend/base_query_not_overridden/violation-02/expected-findings.json +7 -0
  88. package/corpus/backend/base_query_not_overridden/violation-02/modules/notes/service.py +8 -0
  89. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/models.py +6 -0
  90. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/module.py +7 -0
  91. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/router.py +3 -0
  92. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/schemas.py +10 -0
  93. package/corpus/backend/canonical_module_shape/compliant-01/modules/notes/service.py +5 -0
  94. package/corpus/backend/canonical_module_shape/violation-01/modules/notes/router.py +3 -0
  95. package/corpus/backend/datetime_columns_are_timezone_aware/compliant-01/modules/notes/models.py +12 -0
  96. package/corpus/backend/datetime_columns_are_timezone_aware/violation-01/modules/notes/models.py +12 -0
  97. package/corpus/backend/datetime_columns_are_timezone_aware/violation-02/modules/notes/models.py +13 -0
  98. package/corpus/backend/datetime_columns_are_timezone_aware/violation-03/modules/notes/models.py +15 -0
  99. package/corpus/backend/escape_hatch_budget/compliant-01/escape-hatch-budget.json +3 -0
  100. package/corpus/backend/escape_hatch_budget/compliant-01/modules/notes/service.py +2 -0
  101. package/corpus/backend/escape_hatch_budget/compliant-02/escape-hatch-budget.json +1 -0
  102. package/corpus/backend/escape_hatch_budget/compliant-02/modules/notes/service.py +4 -0
  103. package/corpus/backend/escape_hatch_budget/violation-01/escape-hatch-budget.json +1 -0
  104. package/corpus/backend/escape_hatch_budget/violation-01/modules/notes/service.py +2 -0
  105. package/corpus/backend/escape_hatch_budget/violation-02/escape-hatch-budget.json +3 -0
  106. package/corpus/backend/escape_hatch_budget/violation-02/modules/notes/service.py +4 -0
  107. package/corpus/backend/events_reference_catalog/compliant-01/modules/notes/module.py +9 -0
  108. package/corpus/backend/events_reference_catalog/violation-01/modules/notes/module.py +8 -0
  109. package/corpus/backend/input_schemas_exclude_managed_columns/compliant-01/modules/notes/schemas.py +6 -0
  110. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/expected-findings.json +7 -0
  111. package/corpus/backend/input_schemas_exclude_managed_columns/violation-01/modules/notes/schemas.py +9 -0
  112. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/expected-findings.json +12 -0
  113. package/corpus/backend/input_schemas_exclude_managed_columns/violation-02/modules/notes/router.py +14 -0
  114. package/corpus/backend/input_str_fields_have_max_length/compliant-01/modules/notes/schemas.py +4 -0
  115. package/corpus/backend/input_str_fields_have_max_length/violation-01/modules/notes/schemas.py +2 -0
  116. package/corpus/backend/jobs_reference_catalog/compliant-01/modules/notes/service.py +5 -0
  117. package/corpus/backend/jobs_reference_catalog/violation-01/modules/notes/service.py +2 -0
  118. package/corpus/backend/list_routes_paginate/compliant-01/modules/notes/router.py +6 -0
  119. package/corpus/backend/list_routes_paginate/violation-01/modules/notes/router.py +3 -0
  120. package/corpus/backend/modules_declare_policy/compliant-01/modules/notes/module.py +7 -0
  121. package/corpus/backend/modules_declare_policy/violation-01/modules/notes/module.py +6 -0
  122. package/corpus/backend/mutations_emit_audit/compliant-01/modules/notes/service.py +9 -0
  123. package/corpus/backend/mutations_emit_audit/violation-01/modules/notes/service.py +4 -0
  124. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/module.py +7 -0
  125. package/corpus/backend/mutations_require_write_role/compliant-01/modules/notes/router.py +3 -0
  126. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/module.py +7 -0
  127. package/corpus/backend/mutations_require_write_role/compliant-02/modules/notes/router.py +3 -0
  128. package/corpus/backend/mutations_require_write_role/violation-01/expected-findings.json +7 -0
  129. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/module.py +7 -0
  130. package/corpus/backend/mutations_require_write_role/violation-01/modules/notes/router.py +3 -0
  131. package/corpus/backend/mutations_require_write_role/violation-02/expected-findings.json +7 -0
  132. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/module.py +7 -0
  133. package/corpus/backend/mutations_require_write_role/violation-02/modules/notes/router.py +5 -0
  134. package/corpus/backend/mutations_require_write_role/violation-03/expected-findings.json +7 -0
  135. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/module.py +10 -0
  136. package/corpus/backend/mutations_require_write_role/violation-03/modules/notes/router.py +3 -0
  137. package/corpus/backend/no_adhoc_background_runtime/compliant-01/modules/notes/service.py +9 -0
  138. package/corpus/backend/no_adhoc_background_runtime/violation-01/modules/notes/service.py +5 -0
  139. package/corpus/backend/no_adhoc_background_runtime/violation-02/modules/notes/service.py +3 -0
  140. package/corpus/backend/no_adhoc_config_decrypt/compliant-01/modules/billing/service.py +2 -0
  141. package/corpus/backend/no_adhoc_config_decrypt/violation-01/modules/billing/service.py +2 -0
  142. package/corpus/backend/no_adhoc_logging_config/compliant-01/modules/notes/service.py +3 -0
  143. package/corpus/backend/no_adhoc_logging_config/violation-01/modules/notes/service.py +3 -0
  144. package/corpus/backend/no_adhoc_middleware/compliant-01/main.py +3 -0
  145. package/corpus/backend/no_adhoc_middleware/violation-01/main.py +4 -0
  146. package/corpus/backend/no_adhoc_middleware/violation-02/main.py +5 -0
  147. package/corpus/backend/no_adhoc_permission_literals/compliant-01/modules/notes/module.py +3 -0
  148. package/corpus/backend/no_adhoc_permission_literals/compliant-02/modules/notes/router.py +5 -0
  149. package/corpus/backend/no_adhoc_permission_literals/violation-01/expected-findings.json +7 -0
  150. package/corpus/backend/no_adhoc_permission_literals/violation-01/modules/notes/module.py +1 -0
  151. package/corpus/backend/no_adhoc_permission_literals/violation-02/expected-findings.json +7 -0
  152. package/corpus/backend/no_adhoc_permission_literals/violation-02/modules/notes/router.py +2 -0
  153. package/corpus/backend/no_adhoc_permission_literals/violation-03/expected-findings.json +12 -0
  154. package/corpus/backend/no_adhoc_permission_literals/violation-03/modules/notes/module.py +10 -0
  155. package/corpus/backend/no_app_instantiation/compliant-01/main.py +3 -0
  156. package/corpus/backend/no_app_instantiation/violation-01/main.py +3 -0
  157. package/corpus/backend/no_blocking_sleep/compliant-01/modules/notes/service.py +2 -0
  158. package/corpus/backend/no_blocking_sleep/violation-01/modules/notes/service.py +5 -0
  159. package/corpus/backend/no_cross_module_imports/compliant-01/modules/a/service.py +1 -0
  160. package/corpus/backend/no_cross_module_imports/violation-01/modules/a/service.py +1 -0
  161. package/corpus/backend/no_cross_module_imports/violation-02/modules/a/service.py +1 -0
  162. package/corpus/backend/no_dependency_overrides/compliant-01/main.py +12 -0
  163. package/corpus/backend/no_dependency_overrides/violation-01/main.py +11 -0
  164. package/corpus/backend/no_destructive_migrations/compliant-01/modules/notes/migrations/versions/0001_change.py +3 -0
  165. package/corpus/backend/no_destructive_migrations/compliant-02/modules/notes/migrations/versions/0001_change.py +8 -0
  166. package/corpus/backend/no_destructive_migrations/violation-01/expected-findings.json +7 -0
  167. package/corpus/backend/no_destructive_migrations/violation-01/modules/notes/migrations/versions/0001_change.py +2 -0
  168. package/corpus/backend/no_destructive_migrations/violation-02/expected-findings.json +7 -0
  169. package/corpus/backend/no_destructive_migrations/violation-02/modules/notes/migrations/versions/0001_change.py +2 -0
  170. package/corpus/backend/no_destructive_migrations/violation-03/expected-findings.json +7 -0
  171. package/corpus/backend/no_destructive_migrations/violation-03/modules/notes/migrations/versions/0001_change.py +3 -0
  172. package/corpus/backend/no_destructive_migrations/violation-04/expected-findings.json +12 -0
  173. package/corpus/backend/no_destructive_migrations/violation-04/modules/notes/migrations/versions/0001_change.py +6 -0
  174. package/corpus/backend/no_destructive_migrations/violation-05/expected-findings.json +12 -0
  175. package/corpus/backend/no_destructive_migrations/violation-05/modules/notes/migrations/versions/0001_change.py +3 -0
  176. package/corpus/backend/no_dynamic_sql/compliant-01/modules/notes/service.py +1 -0
  177. package/corpus/backend/no_dynamic_sql/compliant-02/modules/notes/service.py +20 -0
  178. package/corpus/backend/no_dynamic_sql/violation-01/modules/notes/service.py +2 -0
  179. package/corpus/backend/no_dynamic_sql/violation-02/modules/notes/service.py +2 -0
  180. package/corpus/backend/no_dynamic_sql/violation-03/modules/notes/service.py +7 -0
  181. package/corpus/backend/no_dynamic_sql/violation-04/modules/notes/service.py +16 -0
  182. package/corpus/backend/no_empty_tests/compliant-01/tests/test_notes.py +2 -0
  183. package/corpus/backend/no_empty_tests/violation-01/tests/test_notes.py +2 -0
  184. package/corpus/backend/no_eval_or_exec/compliant-01/modules/notes/service.py +2 -0
  185. package/corpus/backend/no_eval_or_exec/violation-01/modules/notes/service.py +2 -0
  186. package/corpus/backend/no_hardcoded_credentials/compliant-01/modules/billing/service.py +3 -0
  187. package/corpus/backend/no_hardcoded_credentials/compliant-02/modules/billing/service.py +18 -0
  188. package/corpus/backend/no_hardcoded_credentials/violation-01/expected-findings.json +7 -0
  189. package/corpus/backend/no_hardcoded_credentials/violation-01/modules/billing/service.py +2 -0
  190. package/corpus/backend/no_hardcoded_credentials/violation-02/expected-findings.json +7 -0
  191. package/corpus/backend/no_hardcoded_credentials/violation-02/modules/billing/service.py +1 -0
  192. package/corpus/backend/no_hardcoded_credentials/violation-03/expected-findings.json +22 -0
  193. package/corpus/backend/no_hardcoded_credentials/violation-03/modules/billing/service.py +11 -0
  194. package/corpus/backend/no_hardcoded_credentials/violation-04/expected-findings.json +12 -0
  195. package/corpus/backend/no_hardcoded_credentials/violation-04/modules/billing/service.py +8 -0
  196. package/corpus/backend/no_hardcoded_credentials/violation-05/expected-findings.json +12 -0
  197. package/corpus/backend/no_hardcoded_credentials/violation-05/modules/billing/service.py +4 -0
  198. package/corpus/backend/no_internal_imports/compliant-01/modules/notes/service.py +1 -0
  199. package/corpus/backend/no_internal_imports/violation-01/modules/notes/service.py +1 -0
  200. package/corpus/backend/no_manual_actor_stamping/compliant-01/modules/notes/service.py +5 -0
  201. package/corpus/backend/no_manual_actor_stamping/compliant-02/modules/notes/schemas.py +12 -0
  202. package/corpus/backend/no_manual_actor_stamping/violation-01/modules/notes/service.py +3 -0
  203. package/corpus/backend/no_manual_actor_stamping/violation-02/expected-findings.json +7 -0
  204. package/corpus/backend/no_manual_actor_stamping/violation-02/modules/notes/service.py +4 -0
  205. package/corpus/backend/no_manual_ownership_checks/compliant-01/modules/journals/service.py +3 -0
  206. package/corpus/backend/no_manual_ownership_checks/compliant-02/modules/journals/schemas.py +11 -0
  207. package/corpus/backend/no_manual_ownership_checks/violation-01/modules/journals/service.py +3 -0
  208. package/corpus/backend/no_manual_ownership_checks/violation-02/expected-findings.json +7 -0
  209. package/corpus/backend/no_manual_ownership_checks/violation-02/modules/journals/service.py +9 -0
  210. package/corpus/backend/no_manual_ownership_checks/violation-03/expected-findings.json +7 -0
  211. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/jobs.py +1 -0
  212. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/models.py +7 -0
  213. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/module.py +11 -0
  214. package/corpus/backend/no_manual_ownership_checks/violation-03/modules/notes/service.py +7 -0
  215. package/corpus/backend/no_manual_scope_filtering/compliant-01/modules/notes/service.py +8 -0
  216. package/corpus/backend/no_manual_scope_filtering/compliant-02/modules/notes/schemas.py +12 -0
  217. package/corpus/backend/no_manual_scope_filtering/violation-01/modules/notes/service.py +2 -0
  218. package/corpus/backend/no_manual_scope_filtering/violation-02/modules/notes/service.py +2 -0
  219. package/corpus/backend/no_manual_scope_filtering/violation-03/expected-findings.json +7 -0
  220. package/corpus/backend/no_manual_scope_filtering/violation-03/modules/notes/service.py +7 -0
  221. package/corpus/backend/no_manual_table_schema/compliant-01/modules/notes/models.py +6 -0
  222. package/corpus/backend/no_manual_table_schema/violation-01/modules/notes/models.py +8 -0
  223. package/corpus/backend/no_manual_version_assignment/compliant-01/modules/notes/service.py +5 -0
  224. package/corpus/backend/no_manual_version_assignment/violation-01/modules/notes/service.py +3 -0
  225. package/corpus/backend/no_manual_version_assignment/violation-02/modules/notes/service.py +3 -0
  226. package/corpus/backend/no_manual_version_assignment/violation-03/modules/notes/service.py +3 -0
  227. package/corpus/backend/no_mutable_default_args/compliant-01/modules/notes/service.py +3 -0
  228. package/corpus/backend/no_mutable_default_args/violation-01/modules/notes/service.py +2 -0
  229. package/corpus/backend/no_naive_datetime/compliant-01/modules/notes/service.py +2 -0
  230. package/corpus/backend/no_naive_datetime/violation-01/modules/notes/service.py +2 -0
  231. package/corpus/backend/no_naive_datetime/violation-02/modules/notes/service.py +2 -0
  232. package/corpus/backend/no_oversized_python_files/compliant-01/modules/notes/service.py +9 -0
  233. package/corpus/backend/no_oversized_python_files/violation-01/modules/notes/service.py +530 -0
  234. package/corpus/backend/no_print/compliant-01/modules/notes/service.py +7 -0
  235. package/corpus/backend/no_print/violation-01/modules/notes/service.py +2 -0
  236. package/corpus/backend/no_raw_app_routes/compliant-01/main.py +12 -0
  237. package/corpus/backend/no_raw_app_routes/violation-01/main.py +10 -0
  238. package/corpus/backend/no_raw_app_routes/violation-02/main.py +16 -0
  239. package/corpus/backend/no_raw_app_routes/violation-03/main.py +12 -0
  240. package/corpus/backend/no_raw_app_routes/violation-04/main.py +10 -0
  241. package/corpus/backend/no_raw_app_routes/violation-05/main.py +12 -0
  242. package/corpus/backend/no_raw_app_routes/violation-06/main.py +10 -0
  243. package/corpus/backend/no_raw_connection_access/compliant-01/modules/notes/service.py +5 -0
  244. package/corpus/backend/no_raw_connection_access/violation-01/modules/notes/service.py +2 -0
  245. package/corpus/backend/no_raw_connection_access/violation-02/modules/notes/service.py +2 -0
  246. package/corpus/backend/no_raw_file_references/compliant-01/modules/notes/models.py +10 -0
  247. package/corpus/backend/no_raw_file_references/violation-01/modules/notes/models.py +9 -0
  248. package/corpus/backend/no_raw_outbound_http/compliant-01/modules/notes/service.py +2 -0
  249. package/corpus/backend/no_raw_outbound_http/compliant-03/modules/notes/service.py +12 -0
  250. package/corpus/backend/no_raw_outbound_http/violation-01/expected-findings.json +7 -0
  251. package/corpus/backend/no_raw_outbound_http/violation-01/modules/notes/service.py +1 -0
  252. package/corpus/backend/no_raw_outbound_http/violation-02/expected-findings.json +7 -0
  253. package/corpus/backend/no_raw_outbound_http/violation-02/modules/notes/service.py +1 -0
  254. package/corpus/backend/no_raw_outbound_http/violation-03/expected-findings.json +7 -0
  255. package/corpus/backend/no_raw_outbound_http/violation-03/modules/notes/service.py +1 -0
  256. package/corpus/backend/no_raw_outbound_http/violation-04/expected-findings.json +12 -0
  257. package/corpus/backend/no_raw_outbound_http/violation-04/modules/notes/service.py +7 -0
  258. package/corpus/backend/no_raw_outbound_http/violation-05/expected-findings.json +12 -0
  259. package/corpus/backend/no_raw_outbound_http/violation-05/modules/notes/service.py +5 -0
  260. package/corpus/backend/no_raw_outbound_http/violation-06/expected-findings.json +12 -0
  261. package/corpus/backend/no_raw_outbound_http/violation-06/modules/notes/service.py +2 -0
  262. package/corpus/backend/no_raw_session_construction/compliant-01/modules/notes/service.py +3 -0
  263. package/corpus/backend/no_raw_session_construction/violation-01/modules/notes/service.py +3 -0
  264. package/corpus/backend/no_star_imports/compliant-01/modules/notes/service.py +1 -0
  265. package/corpus/backend/no_star_imports/violation-01/modules/notes/service.py +1 -0
  266. package/corpus/backend/no_todo_fixme/compliant-01/modules/notes/service.py +3 -0
  267. package/corpus/backend/no_todo_fixme/violation-01/modules/notes/service.py +3 -0
  268. package/corpus/backend/no_unique_columns_on_soft_delete_models/compliant-01/modules/notes/models.py +17 -0
  269. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/expected-findings.json +7 -0
  270. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-01/modules/notes/models.py +6 -0
  271. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/expected-findings.json +7 -0
  272. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-02/modules/notes/models.py +13 -0
  273. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/expected-findings.json +7 -0
  274. package/corpus/backend/no_unique_columns_on_soft_delete_models/violation-03/modules/notes/models.py +16 -0
  275. package/corpus/backend/offset_queries_declare_ordering/compliant-01/modules/notes/service.py +4 -0
  276. package/corpus/backend/offset_queries_declare_ordering/violation-01/modules/notes/service.py +2 -0
  277. package/corpus/backend/path_id_params_are_uuid/compliant-01/modules/notes/router.py +6 -0
  278. package/corpus/backend/path_id_params_are_uuid/violation-01/modules/notes/router.py +6 -0
  279. package/corpus/backend/policy_refs_resolve/compliant-01/modules/notes/module.py +7 -0
  280. package/corpus/backend/policy_refs_resolve/violation-01/modules/notes/module.py +8 -0
  281. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/module.py +7 -0
  282. package/corpus/backend/public_modules_are_read_only/compliant-01/modules/notes/router.py +3 -0
  283. package/corpus/backend/public_modules_are_read_only/violation-01/expected-findings.json +7 -0
  284. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/module.py +7 -0
  285. package/corpus/backend/public_modules_are_read_only/violation-01/modules/notes/router.py +3 -0
  286. package/corpus/backend/public_modules_are_read_only/violation-02/expected-findings.json +7 -0
  287. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/module.py +7 -0
  288. package/corpus/backend/public_modules_are_read_only/violation-02/modules/notes/router.py +5 -0
  289. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/models.py +6 -0
  290. package/corpus/backend/reads_use_base_query/compliant-01/modules/notes/service.py +8 -0
  291. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/models.py +9 -0
  292. package/corpus/backend/reads_use_base_query/compliant-02/modules/notes/service.py +9 -0
  293. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/models.py +6 -0
  294. package/corpus/backend/reads_use_base_query/violation-01/modules/notes/service.py +9 -0
  295. package/corpus/backend/reads_use_base_query/violation-02/expected-findings.json +7 -0
  296. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/models.py +6 -0
  297. package/corpus/backend/reads_use_base_query/violation-02/modules/notes/service.py +8 -0
  298. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/models.py +6 -0
  299. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/router.py +3 -0
  300. package/corpus/backend/response_model_not_table_model/compliant-01/modules/notes/schemas.py +5 -0
  301. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/models.py +6 -0
  302. package/corpus/backend/response_model_not_table_model/violation-01/modules/notes/router.py +3 -0
  303. package/corpus/backend/routes_declare_response_model/compliant-01/modules/notes/router.py +8 -0
  304. package/corpus/backend/routes_declare_response_model/violation-01/modules/notes/router.py +3 -0
  305. package/corpus/backend/safe_methods_are_read_only/compliant-01/modules/notes/router.py +6 -0
  306. package/corpus/backend/safe_methods_are_read_only/compliant-02/modules/notes/router.py +5 -0
  307. package/corpus/backend/safe_methods_are_read_only/violation-01/expected-findings.json +7 -0
  308. package/corpus/backend/safe_methods_are_read_only/violation-01/modules/notes/router.py +3 -0
  309. package/corpus/backend/safe_methods_are_read_only/violation-02/expected-findings.json +7 -0
  310. package/corpus/backend/safe_methods_are_read_only/violation-02/modules/notes/router.py +5 -0
  311. package/corpus/backend/safe_methods_are_read_only/violation-03/expected-findings.json +7 -0
  312. package/corpus/backend/safe_methods_are_read_only/violation-03/modules/notes/router.py +3 -0
  313. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-01/modules/users/schemas.py +5 -0
  314. package/corpus/backend/schemas_exclude_sensitive_fields/compliant-02/modules/accounts/schemas.py +12 -0
  315. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/expected-findings.json +7 -0
  316. package/corpus/backend/schemas_exclude_sensitive_fields/violation-01/modules/users/schemas.py +3 -0
  317. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/expected-findings.json +7 -0
  318. package/corpus/backend/schemas_exclude_sensitive_fields/violation-02/modules/connectors/router.py +8 -0
  319. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/expected-findings.json +17 -0
  320. package/corpus/backend/schemas_exclude_sensitive_fields/violation-03/modules/integrations/schemas.py +7 -0
  321. package/corpus/backend/session_imported_from_sqlmodel/compliant-01/modules/notes/service.py +5 -0
  322. package/corpus/backend/session_imported_from_sqlmodel/violation-01/modules/notes/service.py +5 -0
  323. package/corpus/backend/table_models_use_base_table/compliant-01/modules/notes/models.py +6 -0
  324. package/corpus/backend/table_models_use_base_table/violation-01/modules/notes/models.py +5 -0
  325. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/migrations/versions/0a1b2c3d4e5f_create_notes_tables.py +22 -0
  326. package/corpus/backend/tables_have_migrations/compliant-01/modules/notes/models.py +6 -0
  327. package/corpus/backend/tables_have_migrations/compliant-02/capabilities/ledger/models.py +6 -0
  328. package/corpus/backend/tables_have_migrations/violation-01/expected-findings.json +7 -0
  329. package/corpus/backend/tables_have_migrations/violation-01/modules/notes/models.py +6 -0
  330. package/corpus/backend/tables_have_migrations/violation-02/expected-findings.json +7 -0
  331. package/corpus/backend/tables_have_migrations/violation-02/modules/notes/models.py +10 -0
  332. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/models.py +6 -0
  333. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-01/modules/projects/service.py +5 -0
  334. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/models.py +8 -0
  335. package/corpus/backend/tenant_scoped_models_use_scoped_service/compliant-02/modules/projects/service.py +5 -0
  336. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/models.py +6 -0
  337. package/corpus/backend/tenant_scoped_models_use_scoped_service/violation-01/modules/projects/service.py +5 -0
  338. package/corpus/backend/ungoverned_escape_hatch/compliant-01/modules/notes/service.py +5 -0
  339. package/corpus/backend/ungoverned_escape_hatch/compliant-02/modules/notes/service.py +4 -0
  340. package/corpus/backend/ungoverned_escape_hatch/violation-01/modules/notes/service.py +2 -0
  341. package/corpus/backend/update_schemas_inherit_base_update_schema/compliant-01/modules/notes/schemas.py +2 -0
  342. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-01/modules/notes/schemas.py +2 -0
  343. package/corpus/backend/update_schemas_inherit_base_update_schema/violation-02/modules/notes/schemas.py +5 -0
  344. package/corpus/frontend/escape-hatch/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  345. package/corpus/frontend/escape-hatch/violation-01/src/modules/widgets/Widget.tsx +4 -0
  346. package/corpus/frontend/escape-hatch/violation-02/src/modules/widgets/Widget.tsx +7 -0
  347. package/corpus/frontend/escape-hatch/violation-03/src/modules/widgets/Widget.tsx +7 -0
  348. package/corpus/frontend/generated-client-only/compliant-01/src/modules/widgets/Widget.tsx +6 -0
  349. package/corpus/frontend/generated-client-only/compliant-02/src/modules/widgets/Widget.tsx +21 -0
  350. package/corpus/frontend/generated-client-only/compliant-03/src/modules/widgets/Widget.tsx +7 -0
  351. package/corpus/frontend/generated-client-only/compliant-04/src/modules/widgets/Widget.tsx +17 -0
  352. package/corpus/frontend/generated-client-only/violation-01/src/modules/widgets/Widget.tsx +3 -0
  353. package/corpus/frontend/generated-client-only/violation-02/src/modules/widgets/Widget.tsx +9 -0
  354. package/corpus/frontend/generated-client-only/violation-03/src/modules/widgets/Widget.tsx +14 -0
  355. package/corpus/frontend/generated-client-only/violation-04/src/modules/widgets/Widget.tsx +6 -0
  356. package/corpus/frontend/layout-contract/compliant-01/layout-contract.json +3 -0
  357. package/corpus/frontend/layout-contract/compliant-01/src/modules/widgets/Widget.tsx +8 -0
  358. package/corpus/frontend/layout-contract/violation-01/layout-contract.json +3 -0
  359. package/corpus/frontend/layout-contract/violation-01/src/modules/widgets/Widget.tsx +8 -0
  360. package/corpus/frontend/no-cross-module-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  361. package/corpus/frontend/no-cross-module-imports/violation-01/src/modules/widgets/Widget.tsx +3 -0
  362. package/corpus/frontend/no-deep-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  363. package/corpus/frontend/no-deep-imports/violation-01/src/modules/widgets/Widget.tsx +2 -0
  364. package/corpus/frontend/no-dom-html-injection/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  365. package/corpus/frontend/no-dom-html-injection/compliant-02/src/modules/widgets/Widget.tsx +11 -0
  366. package/corpus/frontend/no-dom-html-injection/violation-01/src/modules/widgets/Widget.tsx +3 -0
  367. package/corpus/frontend/no-dom-html-injection/violation-02/src/modules/widgets/Widget.tsx +7 -0
  368. package/corpus/frontend/no-dom-html-injection/violation-03/src/modules/widgets/Widget.tsx +12 -0
  369. package/corpus/frontend/no-eval/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  370. package/corpus/frontend/no-eval/compliant-02/src/modules/widgets/Widget.tsx +11 -0
  371. package/corpus/frontend/no-eval/violation-01/src/modules/widgets/Widget.tsx +3 -0
  372. package/corpus/frontend/no-eval/violation-02/src/modules/widgets/Widget.tsx +7 -0
  373. package/corpus/frontend/no-eval/violation-03/src/modules/widgets/Widget.tsx +10 -0
  374. package/corpus/frontend/no-eval/violation-04/src/modules/widgets/Widget.tsx +6 -0
  375. package/corpus/frontend/no-inline-styling/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  376. package/corpus/frontend/no-inline-styling/violation-01/src/modules/widgets/Widget.tsx +3 -0
  377. package/corpus/frontend/no-inline-styling/violation-02/src/modules/widgets/Widget.tsx +6 -0
  378. package/corpus/frontend/no-style-imports/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  379. package/corpus/frontend/no-style-imports/violation-01/src/modules/widgets/Widget.tsx +2 -0
  380. package/corpus/frontend/no-unsafe-href/compliant-01/src/modules/widgets/Widget.tsx +3 -0
  381. package/corpus/frontend/no-unsafe-href/violation-01/src/modules/widgets/Widget.tsx +3 -0
  382. package/corpus/frontend/no-unsafe-target-blank/compliant-01/src/modules/widgets/Widget.tsx +3 -0
  383. package/corpus/frontend/no-unsafe-target-blank/violation-01/src/modules/widgets/Widget.tsx +3 -0
  384. package/corpus/frontend/router-links/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  385. package/corpus/frontend/router-links/violation-01/src/modules/widgets/Widget.tsx +3 -0
  386. package/corpus/frontend/token-styled-elements/compliant-01/src/modules/widgets/Widget.tsx +4 -0
  387. package/corpus/frontend/token-styled-elements/compliant-02/src/modules/widgets/Widget.tsx +4 -0
  388. package/corpus/frontend/token-styled-elements/violation-01/src/modules/widgets/Widget.tsx +3 -0
  389. package/findings.schema.json +40 -0
  390. package/package.json +23 -0
  391. package/restricted-surface.json +10 -0
  392. package/scorecard.schema.json +52 -0
@@ -0,0 +1,5 @@
1
+ from sqlmodel import Session
2
+
3
+
4
+ def note_count(session: Session) -> int:
5
+ return len(session.exec(query).all())
@@ -0,0 +1,5 @@
1
+ from sqlalchemy.orm import Session
2
+
3
+
4
+ def note_count(session: Session) -> int:
5
+ return len(session.exec(query).all())
@@ -0,0 +1,6 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable
3
+
4
+
5
+ class Note(BaseTable, table=True):
6
+ title: str = Field(max_length=200)
@@ -0,0 +1,5 @@
1
+ from sqlmodel import Field, SQLModel
2
+
3
+
4
+ class Note(SQLModel, table=True):
5
+ title: str = Field(max_length=200)
@@ -0,0 +1,22 @@
1
+ """create notes tables
2
+
3
+ Revision ID: 0a1b2c3d4e5f
4
+ Revises:
5
+ """
6
+ from alembic import op
7
+ import sqlalchemy as sa
8
+
9
+ revision = "0a1b2c3d4e5f"
10
+ down_revision = None
11
+
12
+
13
+ def upgrade() -> None:
14
+ op.create_table(
15
+ "note",
16
+ sa.Column("id", sa.Uuid(), primary_key=True),
17
+ sa.Column("title", sa.String(length=200), nullable=False),
18
+ )
19
+
20
+
21
+ def downgrade() -> None:
22
+ op.drop_table("note")
@@ -0,0 +1,6 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable
3
+
4
+
5
+ class Note(BaseTable, table=True):
6
+ title: str = Field(max_length=200)
@@ -0,0 +1,6 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable
3
+
4
+
5
+ class LedgerEntry(BaseTable, table=True):
6
+ amount_cents: int = Field(default=0)
@@ -0,0 +1,7 @@
1
+ [
2
+ {
3
+ "rule": "backend/tables_have_migrations",
4
+ "path": "modules/notes/models.py",
5
+ "line": 5
6
+ }
7
+ ]
@@ -0,0 +1,6 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable
3
+
4
+
5
+ class Note(BaseTable, table=True):
6
+ title: str = Field(max_length=200)
@@ -0,0 +1,7 @@
1
+ [
2
+ {
3
+ "rule": "backend/tables_have_migrations",
4
+ "path": "modules/notes/models.py",
5
+ "line": 5
6
+ }
7
+ ]
@@ -0,0 +1,10 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable
3
+
4
+
5
+ class NoteTag(BaseTable, table=True):
6
+ label: str = Field(max_length=40)
7
+
8
+
9
+ class Note(BaseTable, table=True):
10
+ title: str = Field(max_length=200)
@@ -0,0 +1,6 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable, TenantScopedMixin
3
+
4
+
5
+ class Project(BaseTable, TenantScopedMixin, table=True):
6
+ name: str = Field(max_length=200)
@@ -0,0 +1,5 @@
1
+ from terp.core import TenantScopedService
2
+
3
+
4
+ class ProjectService(TenantScopedService):
5
+ model = Project
@@ -0,0 +1,8 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable
3
+
4
+
5
+ class Attachment(BaseTable, table=True):
6
+ """Not tenant-scoped (no TenantScopedMixin) — a plain BaseService is fine."""
7
+
8
+ name: str = Field(max_length=200)
@@ -0,0 +1,5 @@
1
+ from terp.core import BaseService
2
+
3
+
4
+ class AttachmentService(BaseService):
5
+ model = Attachment
@@ -0,0 +1,6 @@
1
+ from sqlmodel import Field
2
+ from terp.core import BaseTable, TenantScopedMixin
3
+
4
+
5
+ class Project(BaseTable, TenantScopedMixin, table=True):
6
+ name: str = Field(max_length=200)
@@ -0,0 +1,5 @@
1
+ from terp.core import BaseService
2
+
3
+
4
+ class ProjectService(BaseService):
5
+ model = Project
@@ -0,0 +1,5 @@
1
+ from terp.core import BaseService
2
+
3
+
4
+ class NoteService(BaseService):
5
+ model = Note
@@ -0,0 +1,4 @@
1
+ def render_help():
2
+ # Marker-shaped text inside a string is not an opt-out marker: it is neither
3
+ # honoured nor counted, so this file carries no ungoverned marker.
4
+ return "suppress with '# arch-allow-no-dynamic-sql: <reason>' after review"
@@ -0,0 +1,2 @@
1
+ def legacy_report(session, sql):
2
+ return session.execute(text(sql)) # arch-allow-no-dynamic-sql: legacy reporting query, parameterised upstream
@@ -0,0 +1,2 @@
1
+ class NoteUpdate(BaseUpdateSchema):
2
+ title: str
@@ -0,0 +1,2 @@
1
+ class NoteUpdate(BaseSchema):
2
+ title: str
@@ -0,0 +1,5 @@
1
+ class NotePatch(BaseSchema):
2
+ title: str
3
+
4
+
5
+ router = build_crud_router(update_schema=NotePatch)
@@ -0,0 +1,4 @@
1
+ export function Widget() {
2
+ // terp-allow-token-styled-elements: native button needed for a browser extension host
3
+ return <button>save</button>;
4
+ }
@@ -0,0 +1,4 @@
1
+ // terp-allow-token-styled-elements
2
+ export function Widget() {
3
+ return <button>save</button>;
4
+ }
@@ -0,0 +1,7 @@
1
+ export function Widget() {
2
+ // A marker that names no rule with a governed opt-out (a typo, a stale name,
3
+ // or the governance rule's own name) is itself a violation - it can never be
4
+ // budgeted into legitimacy.
5
+ // terp-allow-made-up-rule: stale name
6
+ return null;
7
+ }
@@ -0,0 +1,7 @@
1
+ export function Widget() {
2
+ // A marker spelled with a RETIRED tool-internal rule id (the pre-0.6.0
3
+ // core-id spelling) names no catalog rule, so it waives nothing: the
4
+ // violation underneath still fires and the stale marker is itself reported.
5
+ // terp-allow-no-restricted-syntax: pre-0.6.0 spelling (migrate to the catalog rule name)
6
+ return <button>x</button>;
7
+ }
@@ -0,0 +1,6 @@
1
+ import { useTerpClient } from "@terp/react-core";
2
+ export function Widget() {
3
+ const client = useTerpClient();
4
+ void client;
5
+ return null;
6
+ }
@@ -0,0 +1,21 @@
1
+ import { useTerpClient } from "@terp/react-core";
2
+
3
+ const repository = {
4
+ fetch(id: string): string {
5
+ return id;
6
+ },
7
+ sendBeacon(payload: string): number {
8
+ return payload.length;
9
+ },
10
+ };
11
+
12
+ export function Widget() {
13
+ const client = useTerpClient();
14
+ void client;
15
+ // fetch("/api/notes") would be refused; the typed client is the one egress path.
16
+ const doc = "WebSocket / XMLHttpRequest / EventSource / fetch are refused in app modules";
17
+ void doc;
18
+ void repository.fetch("1");
19
+ void repository.sendBeacon("x");
20
+ return null;
21
+ }
@@ -0,0 +1,7 @@
1
+ export function Widget() {
2
+ // One catalog rule, one marker: it covers every detection path of the egress
3
+ // family (bare and window-qualified alike).
4
+ // terp-allow-generated-client-only: sanctioned probe for a health widget
5
+ void window.fetch("/healthz");
6
+ return null;
7
+ }
@@ -0,0 +1,17 @@
1
+ import { useRealtimeChannel } from "@terp/react-core";
2
+
3
+ type Notice = { sequence: number; text: string };
4
+
5
+ function isNotice(value: unknown): value is Notice {
6
+ if (typeof value !== "object" || value === null) return false;
7
+ const item = value as Record<string, unknown>;
8
+ return typeof item.sequence === "number" && typeof item.text === "string";
9
+ }
10
+
11
+ export function Widget() {
12
+ const notices = useRealtimeChannel({
13
+ channel: "system.notices",
14
+ validate: isNotice,
15
+ });
16
+ return <p>{notices.lastMessage?.text ?? "Waiting"}</p>;
17
+ }
@@ -0,0 +1,3 @@
1
+ export async function load() {
2
+ return fetch("/api/notes");
3
+ }
@@ -0,0 +1,9 @@
1
+ export async function load(path: string) {
2
+ return globalThis.fetch(path);
3
+ }
4
+
5
+ export async function loadComputed(path: string) {
6
+ return window["fetch"](path);
7
+ }
8
+
9
+ export const transport = fetch;
@@ -0,0 +1,14 @@
1
+ export function connect(url: string) {
2
+ return new WebSocket(
3
+ url,
4
+ ["v1"],
5
+ );
6
+ }
7
+
8
+ export function subscribe(url: string) {
9
+ return new window.EventSource(url);
10
+ }
11
+
12
+ export function flush(url: string, data: string) {
13
+ navigator.sendBeacon(url, data);
14
+ }
@@ -0,0 +1,6 @@
1
+ export function transportFactory() {
2
+ const Ctor = XMLHttpRequest;
3
+ return Ctor;
4
+ }
5
+
6
+ export const events = EventSource;
@@ -0,0 +1,3 @@
1
+ {
2
+ "contract": "standard"
3
+ }
@@ -0,0 +1,8 @@
1
+ import { HubCard, HubPage } from "@terp/react-core";
2
+ export function Widget() {
3
+ return (
4
+ <HubPage title="Notes">
5
+ <HubCard title="All notes" to="/notes" />
6
+ </HubPage>
7
+ );
8
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "contract": "standard"
3
+ }
@@ -0,0 +1,8 @@
1
+ import { DetailList, HubPage } from "@terp/react-core";
2
+ export function Widget() {
3
+ return (
4
+ <HubPage title="Notes">
5
+ <DetailList items={[]} />
6
+ </HubPage>
7
+ );
8
+ }
@@ -0,0 +1,4 @@
1
+ import { Button } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Button>ok</Button>;
4
+ }
@@ -0,0 +1,3 @@
1
+ import { x } from "../other/thing";
2
+ export const Widget = () => null;
3
+ void x;
@@ -0,0 +1,4 @@
1
+ import { Button } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Button>ok</Button>;
4
+ }
@@ -0,0 +1,2 @@
1
+ import Button from "@terp/react-core/src/Button";
2
+ export const Widget = () => <Button>x</Button>;
@@ -0,0 +1,4 @@
1
+ import { Button } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Button>ok</Button>;
4
+ }
@@ -0,0 +1,11 @@
1
+ export function show(el: HTMLElement, message: string) {
2
+ // el.innerHTML = message would be refused; assign text, never markup.
3
+ el.innerText = message;
4
+ el.textContent = message;
5
+ }
6
+
7
+ export function preview(box: { innerHTMLPreview: string }, markup: string) {
8
+ box.innerHTMLPreview = markup;
9
+ }
10
+
11
+ export const HINT = "innerHTML / outerHTML / insertAdjacentHTML are refused in app modules";
@@ -0,0 +1,3 @@
1
+ export function write(el: HTMLElement, html: string) {
2
+ el.innerHTML = html;
3
+ }
@@ -0,0 +1,7 @@
1
+ export function writeHeader(header: string) {
2
+ document.write(header);
3
+ }
4
+
5
+ export function writeFooter(footer: string) {
6
+ window.document.writeln(footer);
7
+ }
@@ -0,0 +1,12 @@
1
+ export function append(el: HTMLElement | null, markup: string) {
2
+ el?.insertAdjacentHTML(
3
+ "beforeend",
4
+ markup,
5
+ );
6
+ }
7
+
8
+ export function replace(el: HTMLElement, title: string) {
9
+ el.outerHTML = `
10
+ <section>${title}</section>
11
+ `;
12
+ }
@@ -0,0 +1,4 @@
1
+ import { Button } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Button>ok</Button>;
4
+ }
@@ -0,0 +1,11 @@
1
+ const interpreter = {
2
+ eval(expression: string): number {
3
+ return expression.length;
4
+ },
5
+ };
6
+
7
+ export function describeExpression(expression: string) {
8
+ // eval(expression) would be refused here; this is a member call on a local object.
9
+ const hint = "eval() and new Function() are refused in app modules";
10
+ return `${interpreter.eval(expression)} ${hint}`;
11
+ }
@@ -0,0 +1,3 @@
1
+ export function run(code: string) {
2
+ return eval(code);
3
+ }
@@ -0,0 +1,7 @@
1
+ export function run(code: string) {
2
+ return globalThis.eval(code);
3
+ }
4
+
5
+ export function runQualified(code: string) {
6
+ return window.eval(code);
7
+ }
@@ -0,0 +1,10 @@
1
+ export function compile(body: string) {
2
+ return new Function(
3
+ "input",
4
+ body,
5
+ );
6
+ }
7
+
8
+ export function compileQualified(body: string) {
9
+ return new window.Function("input", body);
10
+ }
@@ -0,0 +1,6 @@
1
+ export function Widget() {
2
+ // Markers live in real comments only: marker-shaped text inside a string
3
+ // neither suppresses the next line nor its own line.
4
+ const doc = "// terp-allow-no-eval: not a comment";
5
+ return eval(doc);
6
+ }
@@ -0,0 +1,4 @@
1
+ import { Button } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Button>ok</Button>;
4
+ }
@@ -0,0 +1,3 @@
1
+ export function Widget() {
2
+ return <div style={{ color: "red" }}>x</div>;
3
+ }
@@ -0,0 +1,6 @@
1
+ export function Widget() {
2
+ // A justified marker for a SIBLING catalog rule sharing the same core lint id
3
+ // never waives this one: the marker names the catalog rule, not the tool rule.
4
+ // terp-allow-token-styled-elements: wrong rule - must not suppress the style attribute
5
+ return <div style={{ color: "#ff0000" }}>save</div>;
6
+ }
@@ -0,0 +1,4 @@
1
+ import { Button } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Button>ok</Button>;
4
+ }
@@ -0,0 +1,2 @@
1
+ import "./widget.css";
2
+ export const Widget = () => null;
@@ -0,0 +1,3 @@
1
+ export function Widget() {
2
+ return <a href="https://example.com" rel="noopener">docs</a>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export function Widget() {
2
+ return <a href="javascript:alert(1)">go</a>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export function Widget() {
2
+ return <a href="https://example.com" target="_blank" rel="noopener">docs</a>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export function Widget() {
2
+ return <a href="https://example.com" target="_blank">docs</a>;
3
+ }
@@ -0,0 +1,4 @@
1
+ import { Link } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Link to="/notes">notes</Link>;
4
+ }
@@ -0,0 +1,3 @@
1
+ export function Widget() {
2
+ return <a href="/notes">notes</a>;
3
+ }
@@ -0,0 +1,4 @@
1
+ import { Button } from "@terp/react-core";
2
+ export function Widget() {
3
+ return <Button>ok</Button>;
4
+ }
@@ -0,0 +1,4 @@
1
+ export function Widget() {
2
+ // terp-allow-token-styled-elements: native button needed for a browser extension host
3
+ return <button>save</button>;
4
+ }
@@ -0,0 +1,3 @@
1
+ export function Widget() {
2
+ return <button>save</button>;
3
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://terp.dev/spec/findings.schema.json",
4
+ "title": "Terp Standard finding",
5
+ "description": "The machine-readable finding a conformant checker emits: an array of these objects per checked tree. The corpus contract is stated over this shape — for a rule under test, every violation-* case yields at least one finding whose `rule` is the catalog id, and every compliant-* case yields none for that rule. Findings are attributed to the stack-neutral catalog id, never a tool-internal rule id.",
6
+ "type": "array",
7
+ "items": {
8
+ "type": "object",
9
+ "required": ["rule", "path"],
10
+ "additionalProperties": false,
11
+ "properties": {
12
+ "rule": {
13
+ "type": "string",
14
+ "description": "The Terp Standard catalog id the finding realises (catalog/<surface>/<rule>.json).",
15
+ "pattern": "^(backend/[a-z0-9_]+|frontend/[a-z0-9-]+)$"
16
+ },
17
+ "path": {
18
+ "type": "string",
19
+ "description": "File path relative to the checked tree's root (the corpus case root, or the app root), with forward slashes."
20
+ },
21
+ "line": {
22
+ "type": "integer",
23
+ "description": "1-based line of the violation, when the checker can locate it.",
24
+ "minimum": 1
25
+ },
26
+ "message": {
27
+ "type": "string",
28
+ "description": "Human-readable explanation; ideally directive (states the compliant fix)."
29
+ },
30
+ "fix_hint": {
31
+ "type": "string",
32
+ "description": "Agent-oriented remediation metadata: the compliant construct that replaces the violating one, sourced from the catalog entry's non-normative `reference` field (or the checker's own knowledge of the sanctioned pattern). Optional — a consumer must not require it."
33
+ },
34
+ "fingerprint": {
35
+ "type": "string",
36
+ "description": "A stable, checker-chosen identifier for THIS violation instance (e.g. a hash over rule + path + violating source construct), so consumers can track a finding across line-shifting edits. Optional; the fingerprint scheme is checker-internal — only stability across reformatting is expected."
37
+ }
38
+ }
39
+ }
40
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@terpjs/spec",
3
+ "version": "0.14.0",
4
+ "description": "The Terp Standard — stack-neutral rule catalog, violation corpus, finding format, and refused-surface declaration (ADRs 0080/0081; packaged per ADR 0082, published per ADR 0086). Data only: consumers resolve the spec root via require.resolve('@terpjs/spec/package.json').",
5
+ "files": [
6
+ "VERSION",
7
+ "findings.schema.json",
8
+ "app-check-report.schema.json",
9
+ "scorecard.schema.json",
10
+ "assurance-profile.schema.json",
11
+ "restricted-surface.json",
12
+ "catalog",
13
+ "corpus"
14
+ ],
15
+ "license": "Apache-2.0",
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/AITT-NL/terp-spec.git"
22
+ }
23
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "title": "Terp Standard - the refused raw frontend surface",
3
+ "description": "The normative, stack-neutral declaration of the raw browser primitives an app module must not author directly. The prohibition is the portable part of the rules that cite this file (token-styled-elements, no-inline-styling, generated-client-only, no-style-imports, no-deep-imports); which sanctioned component or helper answers each primitive is per-stack configuration, recorded non-normatively in the catalog entry's 'reference' field. A checker realises this file for its language; the reference adapter's BOUNDARY_SPEC is parity-tested against it.",
4
+ "restrictedElements": ["button", "dialog", "form", "input", "select", "table", "textarea"],
5
+ "restrictedAttributes": ["className", "style"],
6
+ "restrictedGlobals": ["EventSource", "WebSocket", "XMLHttpRequest", "fetch"],
7
+ "restrictedMemberCalls": ["navigator.sendBeacon"],
8
+ "styleImportExtensions": [".css", ".less", ".sass", ".scss", ".styl"],
9
+ "deepImportPathSegments": ["dist", "src"]
10
+ }