@graview/core 0.1.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 (327) hide show
  1. package/LICENSE +96 -0
  2. package/README.md +35 -0
  3. package/dist/app.d.ts +294 -0
  4. package/dist/app.d.ts.map +1 -0
  5. package/dist/app.js +49 -0
  6. package/dist/app.js.map +1 -0
  7. package/dist/arrange.d.ts +185 -0
  8. package/dist/arrange.d.ts.map +1 -0
  9. package/dist/arrange.js +456 -0
  10. package/dist/arrange.js.map +1 -0
  11. package/dist/beginning.d.ts +65 -0
  12. package/dist/beginning.d.ts.map +1 -0
  13. package/dist/beginning.js +94 -0
  14. package/dist/beginning.js.map +1 -0
  15. package/dist/bind.d.ts +42 -0
  16. package/dist/bind.d.ts.map +1 -0
  17. package/dist/bind.js +26 -0
  18. package/dist/bind.js.map +1 -0
  19. package/dist/city.d.ts +93 -0
  20. package/dist/city.d.ts.map +1 -0
  21. package/dist/city.js +160 -0
  22. package/dist/city.js.map +1 -0
  23. package/dist/cli/check/arrangement.d.ts +13 -0
  24. package/dist/cli/check/arrangement.d.ts.map +1 -0
  25. package/dist/cli/check/arrangement.js +63 -0
  26. package/dist/cli/check/arrangement.js.map +1 -0
  27. package/dist/cli/check/beginnings.d.ts +16 -0
  28. package/dist/cli/check/beginnings.d.ts.map +1 -0
  29. package/dist/cli/check/beginnings.js +53 -0
  30. package/dist/cli/check/beginnings.js.map +1 -0
  31. package/dist/cli/check/brand.d.ts +17 -0
  32. package/dist/cli/check/brand.d.ts.map +1 -0
  33. package/dist/cli/check/brand.js +153 -0
  34. package/dist/cli/check/brand.js.map +1 -0
  35. package/dist/cli/check/city.d.ts +17 -0
  36. package/dist/cli/check/city.d.ts.map +1 -0
  37. package/dist/cli/check/city.js +58 -0
  38. package/dist/cli/check/city.js.map +1 -0
  39. package/dist/cli/check/context.d.ts +50 -0
  40. package/dist/cli/check/context.d.ts.map +1 -0
  41. package/dist/cli/check/context.js +37 -0
  42. package/dist/cli/check/context.js.map +1 -0
  43. package/dist/cli/check/fields.d.ts +16 -0
  44. package/dist/cli/check/fields.d.ts.map +1 -0
  45. package/dist/cli/check/fields.js +237 -0
  46. package/dist/cli/check/fields.js.map +1 -0
  47. package/dist/cli/check/index.d.ts +14 -0
  48. package/dist/cli/check/index.d.ts.map +1 -0
  49. package/dist/cli/check/index.js +14 -0
  50. package/dist/cli/check/index.js.map +1 -0
  51. package/dist/cli/check/intelligence.d.ts +17 -0
  52. package/dist/cli/check/intelligence.d.ts.map +1 -0
  53. package/dist/cli/check/intelligence.js +136 -0
  54. package/dist/cli/check/intelligence.js.map +1 -0
  55. package/dist/cli/check/lenses.d.ts +17 -0
  56. package/dist/cli/check/lenses.d.ts.map +1 -0
  57. package/dist/cli/check/lenses.js +239 -0
  58. package/dist/cli/check/lenses.js.map +1 -0
  59. package/dist/cli/check/migrations.d.ts +16 -0
  60. package/dist/cli/check/migrations.d.ts.map +1 -0
  61. package/dist/cli/check/migrations.js +43 -0
  62. package/dist/cli/check/migrations.js.map +1 -0
  63. package/dist/cli/check/modules.d.ts +16 -0
  64. package/dist/cli/check/modules.d.ts.map +1 -0
  65. package/dist/cli/check/modules.js +123 -0
  66. package/dist/cli/check/modules.js.map +1 -0
  67. package/dist/cli/check/policy.d.ts +16 -0
  68. package/dist/cli/check/policy.d.ts.map +1 -0
  69. package/dist/cli/check/policy.js +90 -0
  70. package/dist/cli/check/policy.js.map +1 -0
  71. package/dist/cli/check/relations.d.ts +29 -0
  72. package/dist/cli/check/relations.d.ts.map +1 -0
  73. package/dist/cli/check/relations.js +279 -0
  74. package/dist/cli/check/relations.js.map +1 -0
  75. package/dist/cli/check/routes.d.ts +16 -0
  76. package/dist/cli/check/routes.d.ts.map +1 -0
  77. package/dist/cli/check/routes.js +31 -0
  78. package/dist/cli/check/routes.js.map +1 -0
  79. package/dist/cli/check/settings.d.ts +16 -0
  80. package/dist/cli/check/settings.d.ts.map +1 -0
  81. package/dist/cli/check/settings.js +81 -0
  82. package/dist/cli/check/settings.js.map +1 -0
  83. package/dist/cli/check.d.ts +36 -0
  84. package/dist/cli/check.d.ts.map +1 -0
  85. package/dist/cli/check.js +76 -0
  86. package/dist/cli/check.js.map +1 -0
  87. package/dist/cli/create.d.ts +20 -0
  88. package/dist/cli/create.d.ts.map +1 -0
  89. package/dist/cli/create.js +251 -0
  90. package/dist/cli/create.js.map +1 -0
  91. package/dist/cli/describe.d.ts +35 -0
  92. package/dist/cli/describe.d.ts.map +1 -0
  93. package/dist/cli/describe.js +230 -0
  94. package/dist/cli/describe.js.map +1 -0
  95. package/dist/cli/docs.d.ts +11 -0
  96. package/dist/cli/docs.d.ts.map +1 -0
  97. package/dist/cli/docs.js +152 -0
  98. package/dist/cli/docs.js.map +1 -0
  99. package/dist/cli/index.d.ts +18 -0
  100. package/dist/cli/index.d.ts.map +1 -0
  101. package/dist/cli/index.js +281 -0
  102. package/dist/cli/index.js.map +1 -0
  103. package/dist/extend.d.ts +23 -0
  104. package/dist/extend.d.ts.map +1 -0
  105. package/dist/extend.js +53 -0
  106. package/dist/extend.js.map +1 -0
  107. package/dist/graph/diff.d.ts +31 -0
  108. package/dist/graph/diff.d.ts.map +1 -0
  109. package/dist/graph/diff.js +71 -0
  110. package/dist/graph/diff.js.map +1 -0
  111. package/dist/graph/graph.d.ts +69 -0
  112. package/dist/graph/graph.d.ts.map +1 -0
  113. package/dist/graph/graph.js +284 -0
  114. package/dist/graph/graph.js.map +1 -0
  115. package/dist/graph/primitives.d.ts +52 -0
  116. package/dist/graph/primitives.d.ts.map +1 -0
  117. package/dist/graph/primitives.js +62 -0
  118. package/dist/graph/primitives.js.map +1 -0
  119. package/dist/graph/tracked.d.ts +31 -0
  120. package/dist/graph/tracked.d.ts.map +1 -0
  121. package/dist/graph/tracked.js +69 -0
  122. package/dist/graph/tracked.js.map +1 -0
  123. package/dist/graph/types.d.ts +34 -0
  124. package/dist/graph/types.d.ts.map +1 -0
  125. package/dist/graph/types.js +0 -0
  126. package/dist/graph/types.js.map +1 -0
  127. package/dist/index.d.ts +100 -0
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +90 -0
  130. package/dist/index.js.map +1 -0
  131. package/dist/installation.d.ts +112 -0
  132. package/dist/installation.d.ts.map +1 -0
  133. package/dist/installation.js +193 -0
  134. package/dist/installation.js.map +1 -0
  135. package/dist/intelligence-bridge.d.ts +73 -0
  136. package/dist/intelligence-bridge.d.ts.map +1 -0
  137. package/dist/intelligence-bridge.js +31 -0
  138. package/dist/intelligence-bridge.js.map +1 -0
  139. package/dist/invariants/engine.d.ts +36 -0
  140. package/dist/invariants/engine.d.ts.map +1 -0
  141. package/dist/invariants/engine.js +88 -0
  142. package/dist/invariants/engine.js.map +1 -0
  143. package/dist/invariants/types.d.ts +75 -0
  144. package/dist/invariants/types.d.ts.map +1 -0
  145. package/dist/invariants/types.js +2 -0
  146. package/dist/invariants/types.js.map +1 -0
  147. package/dist/modules.d.ts +75 -0
  148. package/dist/modules.d.ts.map +1 -0
  149. package/dist/modules.js +87 -0
  150. package/dist/modules.js.map +1 -0
  151. package/dist/mutations/decidable.d.ts +26 -0
  152. package/dist/mutations/decidable.d.ts.map +1 -0
  153. package/dist/mutations/decidable.js +39 -0
  154. package/dist/mutations/decidable.js.map +1 -0
  155. package/dist/mutations/define-mutation.d.ts +32 -0
  156. package/dist/mutations/define-mutation.d.ts.map +1 -0
  157. package/dist/mutations/define-mutation.js +181 -0
  158. package/dist/mutations/define-mutation.js.map +1 -0
  159. package/dist/mutations/derive-edits.d.ts +75 -0
  160. package/dist/mutations/derive-edits.d.ts.map +1 -0
  161. package/dist/mutations/derive-edits.js +240 -0
  162. package/dist/mutations/derive-edits.js.map +1 -0
  163. package/dist/mutations/form.d.ts +63 -0
  164. package/dist/mutations/form.d.ts.map +1 -0
  165. package/dist/mutations/form.js +113 -0
  166. package/dist/mutations/form.js.map +1 -0
  167. package/dist/mutations/node-ref.d.ts +83 -0
  168. package/dist/mutations/node-ref.d.ts.map +1 -0
  169. package/dist/mutations/node-ref.js +113 -0
  170. package/dist/mutations/node-ref.js.map +1 -0
  171. package/dist/mutations/types.d.ts +136 -0
  172. package/dist/mutations/types.d.ts.map +1 -0
  173. package/dist/mutations/types.js +2 -0
  174. package/dist/mutations/types.js.map +1 -0
  175. package/dist/ops/log.d.ts +35 -0
  176. package/dist/ops/log.d.ts.map +1 -0
  177. package/dist/ops/log.js +103 -0
  178. package/dist/ops/log.js.map +1 -0
  179. package/dist/ops/types.d.ts +52 -0
  180. package/dist/ops/types.d.ts.map +1 -0
  181. package/dist/ops/types.js +2 -0
  182. package/dist/ops/types.js.map +1 -0
  183. package/dist/ops/undo.d.ts +33 -0
  184. package/dist/ops/undo.d.ts.map +1 -0
  185. package/dist/ops/undo.js +65 -0
  186. package/dist/ops/undo.js.map +1 -0
  187. package/dist/permissions/policy.d.ts +68 -0
  188. package/dist/permissions/policy.d.ts.map +1 -0
  189. package/dist/permissions/policy.js +191 -0
  190. package/dist/permissions/policy.js.map +1 -0
  191. package/dist/permissions/types.d.ts +78 -0
  192. package/dist/permissions/types.d.ts.map +1 -0
  193. package/dist/permissions/types.js +17 -0
  194. package/dist/permissions/types.js.map +1 -0
  195. package/dist/persistence/memory.d.ts +6 -0
  196. package/dist/persistence/memory.d.ts.map +1 -0
  197. package/dist/persistence/memory.js +31 -0
  198. package/dist/persistence/memory.js.map +1 -0
  199. package/dist/persistence/sqlite.d.ts +59 -0
  200. package/dist/persistence/sqlite.d.ts.map +1 -0
  201. package/dist/persistence/sqlite.js +131 -0
  202. package/dist/persistence/sqlite.js.map +1 -0
  203. package/dist/persistence/types.d.ts +22 -0
  204. package/dist/persistence/types.d.ts.map +1 -0
  205. package/dist/persistence/types.js +2 -0
  206. package/dist/persistence/types.js.map +1 -0
  207. package/dist/presence.d.ts +38 -0
  208. package/dist/presence.d.ts.map +1 -0
  209. package/dist/presence.js +50 -0
  210. package/dist/presence.js.map +1 -0
  211. package/dist/scaffold/domain.d.ts +8 -0
  212. package/dist/scaffold/domain.d.ts.map +1 -0
  213. package/dist/scaffold/domain.js +331 -0
  214. package/dist/scaffold/domain.js.map +1 -0
  215. package/dist/scaffold/index.d.ts +66 -0
  216. package/dist/scaffold/index.d.ts.map +1 -0
  217. package/dist/scaffold/index.js +142 -0
  218. package/dist/scaffold/index.js.map +1 -0
  219. package/dist/scaffold/lens.d.ts +31 -0
  220. package/dist/scaffold/lens.d.ts.map +1 -0
  221. package/dist/scaffold/lens.js +176 -0
  222. package/dist/scaffold/lens.js.map +1 -0
  223. package/dist/scaffold/names.d.ts +44 -0
  224. package/dist/scaffold/names.d.ts.map +1 -0
  225. package/dist/scaffold/names.js +69 -0
  226. package/dist/scaffold/names.js.map +1 -0
  227. package/dist/scaffold/project.d.ts +41 -0
  228. package/dist/scaffold/project.d.ts.map +1 -0
  229. package/dist/scaffold/project.js +604 -0
  230. package/dist/scaffold/project.js.map +1 -0
  231. package/dist/scaffold/ui.d.ts +15 -0
  232. package/dist/scaffold/ui.d.ts.map +1 -0
  233. package/dist/scaffold/ui.js +536 -0
  234. package/dist/scaffold/ui.js.map +1 -0
  235. package/dist/schema/define-node.d.ts +132 -0
  236. package/dist/schema/define-node.d.ts.map +1 -0
  237. package/dist/schema/define-node.js +254 -0
  238. package/dist/schema/define-node.js.map +1 -0
  239. package/dist/schema/figures.d.ts +42 -0
  240. package/dist/schema/figures.d.ts.map +1 -0
  241. package/dist/schema/figures.js +220 -0
  242. package/dist/schema/figures.js.map +1 -0
  243. package/dist/schema/json-schema.d.ts +39 -0
  244. package/dist/schema/json-schema.d.ts.map +1 -0
  245. package/dist/schema/json-schema.js +74 -0
  246. package/dist/schema/json-schema.js.map +1 -0
  247. package/dist/schema/schema.d.ts +43 -0
  248. package/dist/schema/schema.d.ts.map +1 -0
  249. package/dist/schema/schema.js +102 -0
  250. package/dist/schema/schema.js.map +1 -0
  251. package/dist/schema/types.d.ts +201 -0
  252. package/dist/schema/types.d.ts.map +1 -0
  253. package/dist/schema/types.js +2 -0
  254. package/dist/schema/types.js.map +1 -0
  255. package/dist/search.d.ts +215 -0
  256. package/dist/search.d.ts.map +1 -0
  257. package/dist/search.js +449 -0
  258. package/dist/search.js.map +1 -0
  259. package/dist/settings.d.ts +30 -0
  260. package/dist/settings.d.ts.map +1 -0
  261. package/dist/settings.js +74 -0
  262. package/dist/settings.js.map +1 -0
  263. package/dist/store.d.ts +284 -0
  264. package/dist/store.d.ts.map +1 -0
  265. package/dist/store.js +660 -0
  266. package/dist/store.js.map +1 -0
  267. package/dist/studio-door.d.ts +187 -0
  268. package/dist/studio-door.d.ts.map +1 -0
  269. package/dist/studio-door.js +20 -0
  270. package/dist/studio-door.js.map +1 -0
  271. package/dist/sync/conflict.d.ts +37 -0
  272. package/dist/sync/conflict.d.ts.map +1 -0
  273. package/dist/sync/conflict.js +56 -0
  274. package/dist/sync/conflict.js.map +1 -0
  275. package/dist/sync/engine.d.ts +118 -0
  276. package/dist/sync/engine.d.ts.map +1 -0
  277. package/dist/sync/engine.js +341 -0
  278. package/dist/sync/engine.js.map +1 -0
  279. package/dist/sync/google-calendar.d.ts +54 -0
  280. package/dist/sync/google-calendar.d.ts.map +1 -0
  281. package/dist/sync/google-calendar.js +167 -0
  282. package/dist/sync/google-calendar.js.map +1 -0
  283. package/dist/sync/types.d.ts +125 -0
  284. package/dist/sync/types.d.ts.map +1 -0
  285. package/dist/sync/types.js +6 -0
  286. package/dist/sync/types.js.map +1 -0
  287. package/dist/temporal/effectivity.d.ts +75 -0
  288. package/dist/temporal/effectivity.d.ts.map +1 -0
  289. package/dist/temporal/effectivity.js +89 -0
  290. package/dist/temporal/effectivity.js.map +1 -0
  291. package/dist/testing.d.ts +63 -0
  292. package/dist/testing.d.ts.map +1 -0
  293. package/dist/testing.js +194 -0
  294. package/dist/testing.js.map +1 -0
  295. package/dist/theme/contrast.d.ts +47 -0
  296. package/dist/theme/contrast.d.ts.map +1 -0
  297. package/dist/theme/contrast.js +172 -0
  298. package/dist/theme/contrast.js.map +1 -0
  299. package/dist/theme/derive.d.ts +81 -0
  300. package/dist/theme/derive.d.ts.map +1 -0
  301. package/dist/theme/derive.js +171 -0
  302. package/dist/theme/derive.js.map +1 -0
  303. package/dist/theme/kit.d.ts +106 -0
  304. package/dist/theme/kit.d.ts.map +1 -0
  305. package/dist/theme/kit.js +84 -0
  306. package/dist/theme/kit.js.map +1 -0
  307. package/dist/theme/palettes.d.ts +32 -0
  308. package/dist/theme/palettes.d.ts.map +1 -0
  309. package/dist/theme/palettes.js +96 -0
  310. package/dist/theme/palettes.js.map +1 -0
  311. package/dist/theme/types.d.ts +131 -0
  312. package/dist/theme/types.d.ts.map +1 -0
  313. package/dist/theme/types.js +23 -0
  314. package/dist/theme/types.js.map +1 -0
  315. package/dist/views/types.d.ts +89 -0
  316. package/dist/views/types.d.ts.map +1 -0
  317. package/dist/views/types.js +88 -0
  318. package/dist/views/types.js.map +1 -0
  319. package/dist/watched.d.ts +33 -0
  320. package/dist/watched.d.ts.map +1 -0
  321. package/dist/watched.js +50 -0
  322. package/dist/watched.js.map +1 -0
  323. package/dist/who.d.ts +26 -0
  324. package/dist/who.d.ts.map +1 -0
  325. package/dist/who.js +20 -0
  326. package/dist/who.js.map +1 -0
  327. package/package.json +77 -0
@@ -0,0 +1,123 @@
1
+ export function checkModules(ctx) {
2
+ const { app, kinds, mutations, invariants, add } = ctx;
3
+ /*
4
+ * MODULE BOUNDARIES hold statically, before any workspace flips a toggle.
5
+ *
6
+ * The runtime is forgiving — enabling a module drags its requirements in —
7
+ * so the strict reading lives here: a module naming things nobody
8
+ * declared, a requirement naming a module nobody wrote, and the leak that
9
+ * only shows the day someone turns a module off: an always-on kind whose
10
+ * edge reaches into a module. Ownership by kind, so the reasoning is
11
+ * checkable per edge.
12
+ */
13
+ const moduleEntries = Object.entries(app.modules ?? {});
14
+ const owners = new Map();
15
+ for (const [name, module] of moduleEntries) {
16
+ for (const kind of module.kinds ?? []) {
17
+ const set = owners.get(kind) ?? new Set();
18
+ set.add(name);
19
+ owners.set(kind, set);
20
+ }
21
+ }
22
+ const reach = (name) => {
23
+ const seen = new Set();
24
+ const walk = (current) => {
25
+ if (seen.has(current))
26
+ return;
27
+ seen.add(current);
28
+ for (const required of (app.modules ?? {})[current]?.requires ?? [])
29
+ walk(required);
30
+ };
31
+ walk(name);
32
+ return seen;
33
+ };
34
+ for (const [name, module] of moduleEntries) {
35
+ for (const kind of module.kinds ?? []) {
36
+ if (!kinds.has(kind)) {
37
+ add({
38
+ severity: "error",
39
+ code: "module-unknown-kind",
40
+ where: `modules["${name}"].kinds`,
41
+ message: `Module "${name}" claims kind "${kind}", which no defineNode declares.`,
42
+ fix: `Declare the kind, or remove it from the module.`,
43
+ });
44
+ }
45
+ }
46
+ for (const mutation of module.mutations ?? []) {
47
+ if (!mutations.has(mutation)) {
48
+ add({
49
+ severity: "error",
50
+ code: "module-unknown-mutation",
51
+ where: `modules["${name}"].mutations`,
52
+ message: `Module "${name}" claims mutation "${mutation}", which is not registered.`,
53
+ fix: `Register the mutation, or remove it from the module.`,
54
+ });
55
+ }
56
+ }
57
+ for (const invariant of module.invariants ?? []) {
58
+ if (!invariants.has(invariant)) {
59
+ add({
60
+ severity: "error",
61
+ code: "module-unknown-invariant",
62
+ where: `modules["${name}"].invariants`,
63
+ message: `Module "${name}" claims invariant "${invariant}", which is not registered.`,
64
+ fix: `Register the invariant, or remove it from the module.`,
65
+ });
66
+ }
67
+ }
68
+ for (const required of module.requires ?? []) {
69
+ if (!(required in (app.modules ?? {}))) {
70
+ add({
71
+ severity: "error",
72
+ code: "module-unknown-requirement",
73
+ where: `modules["${name}"].requires`,
74
+ message: `Module "${name}" requires "${required}", which no module declares.`,
75
+ fix: `Declare the module, or drop the requirement.`,
76
+ });
77
+ }
78
+ }
79
+ }
80
+ if (moduleEntries.length > 0) {
81
+ for (const definition of app.schema.definitions) {
82
+ const from = owners.get(definition.kind);
83
+ const fromReach = from === undefined
84
+ ? null
85
+ : new Set([...from].flatMap((name) => [...reach(name)]));
86
+ for (const [edgeKind, edge] of Object.entries(definition.edges)) {
87
+ if (edge.to === "*")
88
+ continue;
89
+ for (const target of edge.to) {
90
+ const targetOwners = owners.get(target);
91
+ if (!targetOwners)
92
+ continue; // core is always on
93
+ /*
94
+ * A module that declares itself required is never off, so no line
95
+ * into it can dangle — the same reason core is skipped one line
96
+ * up. Without this the only answers an app has are to drop the
97
+ * module, losing the thing it is for, or to carry a permanent
98
+ * warning per edge; and a warning that can only ever be
99
+ * acknowledged is one people learn to scroll past, which costs
100
+ * the checker its authority on the warnings that matter.
101
+ */
102
+ if ([...targetOwners].every((owner) => app.modules?.[owner]?.required === true))
103
+ continue;
104
+ const safe = fromReach === null
105
+ ? false // an always-on kind reaching into a module
106
+ : [...targetOwners].some((owner) => fromReach.has(owner));
107
+ if (!safe) {
108
+ add({
109
+ severity: "warning",
110
+ code: "module-edge-leak",
111
+ where: `defineNode("${definition.kind}").edges["${edgeKind}"]`,
112
+ message: `Edge "${edgeKind}" reaches "${target}", owned by module ${[...targetOwners].map((o) => `"${o}"`).join("/")} — the line dangles wherever that module is off.`,
113
+ fix: fromReach === null
114
+ ? `Move "${definition.kind}" into the module, or make the module require nothing this kind depends on.`
115
+ : `Add the target's module to modules["${[...(from ?? [])].join('"/"')}"].requires, or move the edge into the module.`,
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+ //# sourceMappingURL=modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.js","sourceRoot":"","sources":["../../../src/cli/check/modules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { AnySchema } from "../../schema/schema.js";
2
+ /**
3
+ * `note` is a QUESTION ASKED OUT LOUD, not a problem.
4
+ *
5
+ * Some things a checker can see are legitimate designs that the author
6
+ * should nonetheless have looked at once: a lens written for this app and
7
+ * never proved against another domain, a role name two vocabularies both
8
+ * use, a kind unreachable on an empty graph. Filed as warnings they would
9
+ * be warnings that can only ever be acknowledged, and those are the ones
10
+ * people learn to scroll past — which costs the checker its authority on
11
+ * the warnings that matter. So they have their own voice: counted, printed,
12
+ * and never a failure.
13
+ */
14
+ import type { CheckContext } from "./context.js";
15
+ export declare function checkPolicy<S extends AnySchema>(ctx: CheckContext<S>): void;
16
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../../src/cli/check/policy.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAiG3E"}
@@ -0,0 +1,90 @@
1
+ import { permits, rolesOf } from "../../permissions/policy.js";
2
+ import { derivedVia } from "../../mutations/derive-edits.js";
3
+ export function checkPolicy(ctx) {
4
+ const { app, kinds, declaredMutations, mutations, add } = ctx;
5
+ /*
6
+ * A policy that locks somebody out of everything, or locks everybody out of
7
+ * something.
8
+ *
9
+ * Both are mistakes in the declaration rather than at runtime, and both are
10
+ * silent: a mutation no role can run looks exactly like a mutation nobody
11
+ * happens to have needed yet, and a role with nothing to do looks exactly
12
+ * like a role whose grants are elsewhere. The person who finds either is
13
+ * otherwise the person standing in front of a button they cannot press.
14
+ */
15
+ if (app.policy) {
16
+ const roles = rolesOf(app.policy);
17
+ for (const mutation of app.mutations ?? []) {
18
+ const subjectKinds = mutation.subject && mutation.subject.kinds !== "*"
19
+ ? mutation.subject.kinds
20
+ : [undefined];
21
+ const reachable = roles.some((role) => subjectKinds.some(
22
+ // A self grant counts: a role that may edit its own record may do
23
+ // something, so the role is judged as somebody acting on themselves.
24
+ (kind) => permits(app.policy, { kind: "human", id: "themselves", roles: [role] }, mutation.name, kind, undefined, "themselves").ok));
25
+ const openToAll = subjectKinds.some((kind) => permits(app.policy, { kind: "human", roles: [] }, mutation.name, kind).ok);
26
+ if (!reachable && !openToAll) {
27
+ add({
28
+ severity: "error",
29
+ code: "mutation-unreachable-by-any-role",
30
+ where: `policy.grants (mutation "${mutation.name}")`,
31
+ message: `No role may ever run "${mutation.name}", so it is declared and unreachable.`,
32
+ fix: `Grant it to a role, or remove the mutation.${roles.length > 0 ? ` Roles in this policy: ${roles.join(", ")}.` : ""}`,
33
+ });
34
+ }
35
+ }
36
+ for (const role of roles) {
37
+ /*
38
+ * Declared AND derived acts count, and a self grant counts: a role
39
+ * that may edit its own record may do something. The role is judged
40
+ * as somebody acting on themselves, which is the most a self grant
41
+ * ever allows.
42
+ */
43
+ const canDo = [...mutations.values()].some((mutation) => {
44
+ const subjectKinds = mutation.subject && mutation.subject.kinds !== "*"
45
+ ? mutation.subject.kinds
46
+ : [undefined];
47
+ const via = derivedVia(app.schema, declaredMutations, mutation);
48
+ return subjectKinds.some((kind) => permits(app.policy, { kind: "human", id: "themselves", roles: [role] }, mutation.name, kind, via, "themselves").ok);
49
+ });
50
+ if (!canDo) {
51
+ add({
52
+ severity: "warning",
53
+ code: "role-may-do-nothing",
54
+ where: `policy.grants (role "${role}")`,
55
+ message: `"${role}" may run no mutation, so anyone holding it can only read.`,
56
+ fix: "Grant it something, or drop the role if read-only was the intent.",
57
+ });
58
+ }
59
+ }
60
+ for (const grant of app.policy.grants) {
61
+ if (grant.mutations === "*")
62
+ continue;
63
+ for (const name of grant.mutations) {
64
+ if (mutations.has(name))
65
+ continue;
66
+ add({
67
+ severity: "error",
68
+ code: "grant-unknown-mutation",
69
+ where: "policy.grants",
70
+ message: `Grants "${name}", which no mutation declares.`,
71
+ fix: `Register a mutation called "${name}", or drop it from the grant.`,
72
+ });
73
+ }
74
+ if (grant.kinds === undefined || grant.kinds === "*")
75
+ continue;
76
+ for (const kind of grant.kinds) {
77
+ if (kinds.has(kind))
78
+ continue;
79
+ add({
80
+ severity: "error",
81
+ code: "grant-unknown-kind",
82
+ where: "policy.grants",
83
+ message: `Restricted to "${kind}", which no defineNode declares.`,
84
+ fix: `Use one of: ${[...kinds].join(", ")}.`,
85
+ });
86
+ }
87
+ }
88
+ }
89
+ }
90
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/cli/check/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAiB7D,MAAM,UAAU,WAAW,CAAsB,GAAoB;IACnE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IAC9D;;;;;;;;;OASG;IACH,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAClC,KAAK,MAAM,QAAQ,IAAI,GAAG,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;YAC3C,MAAM,YAAY,GAChB,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,KAAK,KAAK,GAAG;gBAChD,CAAC,CAAE,QAAQ,CAAC,OAAO,CAAC,KAA2B;gBAC/C,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAClB,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CACpC,YAAY,CAAC,IAAI;YACf,kEAAkE;YAClE,qEAAqE;YACrE,CAAC,IAAI,EAAE,EAAE,CACP,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,EAAE,CAC3H,CACF,CAAC;YACF,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,CACjC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CACpF,CAAC;YACF,IAAI,CAAC,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC7B,GAAG,CAAC;oBACF,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,kCAAkC;oBACxC,KAAK,EAAE,4BAA4B,QAAQ,CAAC,IAAI,IAAI;oBACpD,OAAO,EAAE,yBAAyB,QAAQ,CAAC,IAAI,uCAAuC;oBACtF,GAAG,EAAE,8CACH,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,0BAA0B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EACrE,EAAE;iBACH,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB;;;;;eAKG;YACH,MAAM,KAAK,GAAG,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;gBACtD,MAAM,YAAY,GAChB,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,KAAK,KAAK,GAAG;oBAChD,CAAC,CAAE,QAAQ,CAAC,OAAO,CAAC,KAA2B;oBAC/C,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBAClB,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE,QAAQ,CAAC,CAAC;gBAChE,OAAO,YAAY,CAAC,IAAI,CACtB,CAAC,IAAI,EAAE,EAAE,CACP,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CACrH,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,GAAG,CAAC;oBACF,QAAQ,EAAE,SAAS;oBACnB,IAAI,EAAE,qBAAqB;oBAC3B,KAAK,EAAE,wBAAwB,IAAI,IAAI;oBACvC,OAAO,EAAE,IAAI,IAAI,4DAA4D;oBAC7E,GAAG,EAAE,mEAAmE;iBACzE,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACtC,IAAI,KAAK,CAAC,SAAS,KAAK,GAAG;gBAAE,SAAS;YACtC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;gBACnC,IAAI,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAClC,GAAG,CAAC;oBACF,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,wBAAwB;oBAC9B,KAAK,EAAE,eAAe;oBACtB,OAAO,EAAE,WAAW,IAAI,gCAAgC;oBACxD,GAAG,EAAE,+BAA+B,IAAI,+BAA+B;iBACxE,CAAC,CAAC;YACL,CAAC;YACD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,GAAG;gBAAE,SAAS;YAC/D,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC/B,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,SAAS;gBAC9B,GAAG,CAAC;oBACF,QAAQ,EAAE,OAAO;oBACjB,IAAI,EAAE,oBAAoB;oBAC1B,KAAK,EAAE,eAAe;oBACtB,OAAO,EAAE,kBAAkB,IAAI,kCAAkC;oBACjE,GAAG,EAAE,eAAe,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;iBAC7C,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,29 @@
1
+ import type { AnySchema } from "../../schema/schema.js";
2
+ /**
3
+ * `note` is a QUESTION ASKED OUT LOUD, not a problem.
4
+ *
5
+ * Some things a checker can see are legitimate designs that the author
6
+ * should nonetheless have looked at once: a lens written for this app and
7
+ * never proved against another domain, a role name two vocabularies both
8
+ * use, a kind unreachable on an empty graph. Filed as warnings they would
9
+ * be warnings that can only ever be acknowledged, and those are the ones
10
+ * people learn to scroll past — which costs the checker its authority on
11
+ * the warnings that matter. So they have their own voice: counted, printed,
12
+ * and never a failure.
13
+ */
14
+ import type { CheckContext } from "./context.js";
15
+ export declare function checkReadings<S extends AnySchema>(ctx: CheckContext<S>): void;
16
+ /**
17
+ * ONE EDGE NAME, ONE RELATION. Every surface treats an edge kind as a single
18
+ * relation: the connections on a record, the pages' groups, the captions
19
+ * over a band, the arrangement's `by:` all key on its name. Declared on two
20
+ * kinds with two sets of words — `by` on a song ("the artist whose song it
21
+ * is" / "their songs") and on an album ("the artist whose release it is" /
22
+ * "their releases") — the artist's page listed thirty-five songs and six
23
+ * albums together under "Their releases". Declaring the same relation from
24
+ * several kinds in the SAME words is fine and is left alone.
25
+ */
26
+ export declare function checkEdgeNamesAgree<S extends AnySchema>(ctx: CheckContext<S>): void;
27
+ export declare function checkActsFromEnds<S extends AnySchema>(ctx: CheckContext<S>, writtenByAModel: ReadonlySet<string>): void;
28
+ export declare function checkUnmakeable<S extends AnySchema>(ctx: CheckContext<S>): void;
29
+ //# sourceMappingURL=relations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../../../src/cli/check/relations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAGjD,wBAAgB,aAAa,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CA8B7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CA0BnF;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CA+JvH;AAED,wBAAgB,eAAe,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CAgD/E"}
@@ -0,0 +1,279 @@
1
+ import { nodeRefArgs } from "../../mutations/node-ref.js";
2
+ import { withArticle } from "../../schema/define-node.js";
3
+ import { bounded } from "./context.js";
4
+ export function checkReadings(ctx) {
5
+ const { app, add } = ctx;
6
+ /*
7
+ * AN EDGE HAS ONE DIRECTION AND TWO READINGS, and every surface reads it
8
+ * from the end it is standing on: a plot's page says "who looks after
9
+ * it", the gardener's page says what she looks after. With only the
10
+ * declaring side's words, the other end is captioned with the edge kind
11
+ * in plain words — "tended by", on the gardener — which reads the wrong
12
+ * way round. The words are part of the declaration, so their absence is
13
+ * a finding here rather than a surprise on a page.
14
+ */
15
+ for (const definition of app.schema.definitions) {
16
+ for (const [edgeKind, edge] of Object.entries(definition.edges)) {
17
+ if (edge.inverse)
18
+ continue;
19
+ const farEnd = edge.to === "*" ? "the other end" : edge.to.map((kind) => withArticle(kind)).join(" or ");
20
+ add({
21
+ severity: "warning",
22
+ code: "edge-without-inverse",
23
+ where: `defineNode("${definition.kind}").edges["${edgeKind}"]`,
24
+ message: edge.description
25
+ ? `"${edgeKind}" reads from ${withArticle(definition.kind)} only ("${edge.description}"); from ${farEnd} it is captioned "${edgeKind.replace(/-/g, " ")}", which is the wrong way round.`
26
+ : `"${edgeKind}" has no words in either direction; both ends are captioned "${edgeKind.replace(/-/g, " ")}".`,
27
+ fix: `Add inverse: "…" — how the relation reads from ${farEnd}` +
28
+ (edge.description ? "" : `, and description: "…" for how it reads from ${withArticle(definition.kind)}`) +
29
+ `.`,
30
+ });
31
+ }
32
+ }
33
+ }
34
+ /**
35
+ * ONE EDGE NAME, ONE RELATION. Every surface treats an edge kind as a single
36
+ * relation: the connections on a record, the pages' groups, the captions
37
+ * over a band, the arrangement's `by:` all key on its name. Declared on two
38
+ * kinds with two sets of words — `by` on a song ("the artist whose song it
39
+ * is" / "their songs") and on an album ("the artist whose release it is" /
40
+ * "their releases") — the artist's page listed thirty-five songs and six
41
+ * albums together under "Their releases". Declaring the same relation from
42
+ * several kinds in the SAME words is fine and is left alone.
43
+ */
44
+ export function checkEdgeNamesAgree(ctx) {
45
+ const { app, add } = ctx;
46
+ const declared = new Map();
47
+ for (const definition of app.schema.definitions) {
48
+ for (const [edgeKind, edge] of Object.entries(definition.edges)) {
49
+ const held = declared.get(edgeKind) ?? [];
50
+ held.push({ kind: definition.kind, ...(edge.description ? { description: edge.description } : {}), ...(edge.inverse ? { inverse: edge.inverse } : {}) });
51
+ declared.set(edgeKind, held);
52
+ }
53
+ }
54
+ for (const [edgeKind, ends] of declared) {
55
+ if (ends.length < 2)
56
+ continue;
57
+ const words = new Set(ends.map((end) => `${end.description ?? ""}|${end.inverse ?? ""}`));
58
+ if (words.size < 2)
59
+ continue;
60
+ const said = ends
61
+ .map((end) => `on ${withArticle(end.kind)} ("${end.description ?? edgeKind}" / "${end.inverse ?? edgeKind}")`)
62
+ .join(" and ");
63
+ const [first, second] = ends;
64
+ add({
65
+ severity: "error",
66
+ code: "edge-name-shared",
67
+ where: ends.map((end) => `defineNode("${end.kind}").edges["${edgeKind}"]`).join(", "),
68
+ message: `"${edgeKind}" is declared ${said}, but to every surface an edge name is ONE relation: from the far end both are listed together under ${JSON.stringify(first.inverse ?? edgeKind)}, including the ${second.kind === first.kind ? "second" : `${second.kind}s`}.`,
69
+ fix: `Give each relation its own name (say "${edgeKind}" on ${withArticle(first.kind)} and "${second.kind}-${edgeKind}" on ${withArticle(second.kind)}), or give every declaration the same description and inverse.`,
70
+ });
71
+ }
72
+ }
73
+ export function checkActsFromEnds(ctx, writtenByAModel) {
74
+ const { app, kinds, mutations, add } = ctx;
75
+ /*
76
+ * AN ACT OFFERED FROM AN END IT HAS NO WORDS FOR.
77
+ *
78
+ * A mutation that declares what it connects or severs is offered from
79
+ * EITHER endpoint — standing on a person, "take this one off the run" is
80
+ * the natural thing to say. The button there is labelled with `title`,
81
+ * which is written from the subject's side: "Hand it to someone", offered
82
+ * on the owner, reads as handing the owner to someone. The same shape as
83
+ * `edge-without-inverse`, one layer up: the relation has two readings and
84
+ * the act only has one.
85
+ */
86
+ for (const mutation of app.mutations ?? []) {
87
+ if (mutation.fromTheOtherEnd)
88
+ continue;
89
+ const subject = mutation.subject;
90
+ const ties = [...(mutation.connects ?? []), ...(mutation.severs ?? [])];
91
+ if (!subject || ties.length === 0 || subject.kinds === "*")
92
+ continue;
93
+ const subjectKinds = subject.kinds;
94
+ // The far ends: node-reference arguments naming a kind the subject is not.
95
+ const farEnds = new Set();
96
+ for (const ref of nodeRefArgs(mutation.input)) {
97
+ if (ref.name === subject.arg)
98
+ continue;
99
+ for (const kind of ref.kinds) {
100
+ if (kind !== "*" && !subjectKinds.includes(kind))
101
+ farEnds.add(kind);
102
+ }
103
+ }
104
+ if (farEnds.size === 0)
105
+ continue;
106
+ const spoken = [...farEnds].map((kind) => withArticle(kind)).join(" or ");
107
+ add({
108
+ severity: "warning",
109
+ code: "act-without-far-end-reading",
110
+ where: `defineMutation("${mutation.name}")`,
111
+ message: `"${mutation.title ?? mutation.name}" is written from ${subjectKinds.map((kind) => withArticle(kind)).join(" or ")}, ` +
112
+ `and is also offered on ${spoken}, where it is labelled with those same words — which is the wrong way round.`,
113
+ fix: `Add fromTheOtherEnd: "…" — how this act reads standing on ${spoken}.`,
114
+ });
115
+ }
116
+ for (const definition of app.schema.definitions) {
117
+ for (const [edgeKind, edge] of Object.entries(definition.edges)) {
118
+ if (edge.to === "*")
119
+ continue;
120
+ for (const target of edge.to) {
121
+ if (!kinds.has(target)) {
122
+ add({
123
+ severity: "error",
124
+ code: "edge-target-undeclared",
125
+ where: `defineNode("${definition.kind}").edges["${edgeKind}"]`,
126
+ message: `Edge "${edgeKind}" points at "${target}", which no defineNode declares.`,
127
+ fix: `Declare a node kind "${target}", or change the target to one of: ${[...kinds].join(", ")}.`,
128
+ });
129
+ }
130
+ }
131
+ }
132
+ /*
133
+ * A LABEL A MODEL WRITES IS A NAME, AND NOTHING SAYS SO.
134
+ *
135
+ * Every derived surface in this framework draws `label`: on a card, in
136
+ * a list, on a plan, in a chip. And the declaration almost always says
137
+ * `z.string().min(1)`, which permits a paragraph. When a person types
138
+ * it that is fine — people write names. When a MODEL writes it, it
139
+ * writes whatever the prompt implied, and the prompt was generated from
140
+ * this schema, which said nothing.
141
+ *
142
+ * A product asked a model to survey a garden and got back seven areas
143
+ * called things like "Pea-gravel corner with river-rock border, log
144
+ * seats and a fire bowl". Every one is a true, useful sentence and a
145
+ * terrible name, and the model was not wrong — it was never told. The
146
+ * plan drew them across each other, the cards wrapped to four lines,
147
+ * and nothing failed anywhere.
148
+ *
149
+ * So this asks only about kinds a declared provider may actually
150
+ * create. A note, not a warning: a long label is legal and sometimes
151
+ * right. What is not defensible is not having made the call.
152
+ */
153
+ if (writtenByAModel.has(definition.kind)) {
154
+ const field = definition.fields.shape["label"];
155
+ if (field !== undefined && !bounded(field)) {
156
+ add({
157
+ severity: "note",
158
+ code: "label-unbounded",
159
+ where: `defineNode("${definition.kind}").fields.label`,
160
+ message: `A declared provider may create ${withArticle(definition.kind)}, and "label" has no maximum length — so a model may write a paragraph where a name goes, and every surface that draws it will try.`,
161
+ fix: `Bound it to something an interface can draw: z.string().min(1).max(60). Whatever else there is to say about ${withArticle(definition.kind)} belongs in a field of its own, where a person will actually read it.`,
162
+ });
163
+ }
164
+ }
165
+ /*
166
+ * A lifecycle must name a real field. A currency read off a field that
167
+ * does not exist would silently make every node current for ever —
168
+ * which is exactly the state a kind was in before declaring anything,
169
+ * minus the honesty.
170
+ */
171
+ if (definition.lifecycle) {
172
+ const shape = definition.fields.shape;
173
+ if (!(definition.lifecycle.field in shape)) {
174
+ add({
175
+ severity: "error",
176
+ code: "lifecycle-missing-field",
177
+ where: `defineNode("${definition.kind}").lifecycle`,
178
+ message: `Lifecycle reads field "${definition.lifecycle.field}", which is not in this kind's fields.`,
179
+ fix: `Point it at one of: ${Object.keys(shape).join(", ")}.`,
180
+ });
181
+ }
182
+ if (definition.lifecycle.retired !== "date" &&
183
+ definition.lifecycle.retired.length === 0) {
184
+ add({
185
+ severity: "error",
186
+ code: "lifecycle-never-retires",
187
+ where: `defineNode("${definition.kind}").lifecycle`,
188
+ message: `Lifecycle lists no retired values, so nothing can ever leave the horizon.`,
189
+ fix: `List the values that mean "past", or use "date" for an expiry field.`,
190
+ });
191
+ }
192
+ }
193
+ for (const [role, field] of Object.entries(definition.fieldRoles ?? {})) {
194
+ const shape = definition.fields.shape;
195
+ if (!(field in shape)) {
196
+ add({
197
+ severity: "error",
198
+ code: "field-role-missing-field",
199
+ where: `defineNode("${definition.kind}").fieldRoles.${role}`,
200
+ message: `Field role "${role}" maps to field "${field}", which is not in this kind's fields.`,
201
+ fix: `Point the role at one of: ${Object.keys(shape).join(", ")}.`,
202
+ });
203
+ }
204
+ }
205
+ }
206
+ for (const invariant of app.invariants ?? []) {
207
+ if (invariant.scope !== "graph" && !kinds.has(invariant.scope.kind)) {
208
+ add({
209
+ severity: "error",
210
+ code: "invariant-scope-undeclared",
211
+ where: `defineInvariant("${invariant.name}").scope`,
212
+ message: `Scoped to node kind "${invariant.scope.kind}", which no defineNode declares.`,
213
+ fix: `Use one of: ${[...kinds].join(", ")}.`,
214
+ });
215
+ }
216
+ for (const repair of invariant.repairs ?? []) {
217
+ if (!mutations.has(repair)) {
218
+ add({
219
+ severity: "error",
220
+ code: "repair-unknown-mutation",
221
+ where: `defineInvariant("${invariant.name}").repairs`,
222
+ message: `Names repair mutation "${repair}", which is not registered on this app.`,
223
+ fix: `Register a mutation called "${repair}", or drop it from repairs. Registered: ${[...mutations.keys()].join(", ") || "(none)"}.`,
224
+ });
225
+ }
226
+ }
227
+ }
228
+ }
229
+ export function checkUnmakeable(ctx) {
230
+ const { app, add } = ctx;
231
+ /*
232
+ * A RELATION YOU CAN MAKE BUT NEVER UNMAKE.
233
+ *
234
+ * An edge kind with a connecting act and no severing one is an asymmetry
235
+ * the declaration exposes: whoever draws the line can never erase it, and
236
+ * nobody notices until the first person needs to. Some edges are like that
237
+ * on purpose — history, rationale, a record — and the way to say so is
238
+ * `appendOnly: true` on the edge declaration, which suppresses this and
239
+ * documents the intent in the same stroke.
240
+ */
241
+ {
242
+ const connectors = new Map();
243
+ const severers = new Set();
244
+ for (const mutation of app.mutations ?? []) {
245
+ for (const edgeKind of mutation.connects ?? []) {
246
+ connectors.set(edgeKind, [...(connectors.get(edgeKind) ?? []), mutation.name]);
247
+ }
248
+ for (const edgeKind of mutation.severs ?? [])
249
+ severers.add(edgeKind);
250
+ }
251
+ for (const [edgeKind, makers] of connectors) {
252
+ if (severers.has(edgeKind))
253
+ continue;
254
+ const declaredOn = app.schema.definitions.filter((definition) => edgeKind in definition.edges);
255
+ // An unknown edge kind is already `edge-claim-unknown-kind` above.
256
+ if (declaredOn.length === 0)
257
+ continue;
258
+ /*
259
+ * EVERY declaration of the edge kind must say appendOnly, not just
260
+ * one. Two kinds can share an edge-kind name, and a suppression on
261
+ * one must not hide the other's makeable-but-never-unmakeable
262
+ * relation — that is the exact asymmetry this check exists to catch.
263
+ */
264
+ if (declaredOn.every((definition) => definition.edges[edgeKind]?.appendOnly))
265
+ continue;
266
+ add({
267
+ severity: "warning",
268
+ code: "edge-without-severer",
269
+ where: `defineMutation("${makers[0]}").connects`,
270
+ message: `"${edgeKind}" is a relation you can make (${makers.join(", ")}) but never unmake — no mutation declares it in severs.`,
271
+ fix: `Declare severs: ["${edgeKind}"] on the act that removes it, or mark the edge ` +
272
+ `appendOnly: true on ${declaredOn
273
+ .map((definition) => `defineNode("${definition.kind}").edges["${edgeKind}"]`)
274
+ .join(" / ")} if it is genuinely never unmade.`,
275
+ });
276
+ }
277
+ }
278
+ }
279
+ //# sourceMappingURL=relations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relations.js","sourceRoot":"","sources":["../../../src/cli/check/relations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { AnySchema } from "../../schema/schema.js";
2
+ /**
3
+ * `note` is a QUESTION ASKED OUT LOUD, not a problem.
4
+ *
5
+ * Some things a checker can see are legitimate designs that the author
6
+ * should nonetheless have looked at once: a lens written for this app and
7
+ * never proved against another domain, a role name two vocabularies both
8
+ * use, a kind unreachable on an empty graph. Filed as warnings they would
9
+ * be warnings that can only ever be acknowledged, and those are the ones
10
+ * people learn to scroll past — which costs the checker its authority on
11
+ * the warnings that matter. So they have their own voice: counted, printed,
12
+ * and never a failure.
13
+ */
14
+ import type { CheckContext } from "./context.js";
15
+ export declare function checkRoutes<S extends AnySchema>(ctx: CheckContext<S>): void;
16
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/cli/check/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,wBAAgB,WAAW,CAAC,CAAC,SAAS,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI,CA6B3E"}
@@ -0,0 +1,31 @@
1
+ export function checkRoutes(ctx) {
2
+ const { app, add } = ctx;
3
+ /*
4
+ * The routed face derives `/:plural` from each kind's plural. Two kinds
5
+ * whose plurals slug identically would leave one of them unreachable by
6
+ * registration order — a route nobody can link to.
7
+ */
8
+ const slugs = new Map();
9
+ for (const definition of app.schema.definitions) {
10
+ const plural = definition.plural ?? `${definition.kind}s`;
11
+ const slug = plural.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
12
+ const taken = slugs.get(slug);
13
+ if (taken) {
14
+ add({
15
+ severity: "error",
16
+ code: "plural-slug-collision",
17
+ where: `defineNode("${definition.kind}").plural`,
18
+ message: `"${plural}" slugs to "/${slug}", already taken by kind "${taken}".`,
19
+ fix: `Give one of them a distinct plural.`,
20
+ });
21
+ }
22
+ else {
23
+ slugs.set(slug, definition.kind);
24
+ }
25
+ }
26
+ /*
27
+ * A declared provider's allowlist must name real mutations — a metering
28
+ * or narrowing rule pointing at nothing enforces nothing.
29
+ */
30
+ }
31
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/cli/check/routes.ts"],"names":[],"mappings":"AAgBA,MAAM,UAAU,WAAW,CAAsB,GAAoB;IACnE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,CAAC;IACzB;;;;OAIG;IACH,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,UAAU,IAAI,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,GAAG,CAAC;QAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpF,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,KAAK,EAAE,CAAC;YACV,GAAG,CAAC;gBACF,QAAQ,EAAE,OAAO;gBACjB,IAAI,EAAE,uBAAuB;gBAC7B,KAAK,EAAE,eAAe,UAAU,CAAC,IAAI,WAAW;gBAChD,OAAO,EAAE,IAAI,MAAM,gBAAgB,IAAI,6BAA6B,KAAK,IAAI;gBAC7E,GAAG,EAAE,qCAAqC;aAC3C,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IAED;;;OAGG;AACL,CAAC"}