@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
package/dist/store.js ADDED
@@ -0,0 +1,660 @@
1
+ import { Graph, GraphError } from "./graph/graph.js";
2
+ import { resolveModules } from "./modules.js";
3
+ import { diffSnapshots } from "./graph/diff.js";
4
+ import { invert, normalise } from "./graph/primitives.js";
5
+ import { evaluate } from "./invariants/engine.js";
6
+ import { compileMutation } from "./mutations/define-mutation.js";
7
+ import { deriveMutations, derivedVia } from "./mutations/derive-edits.js";
8
+ import { OperationLog } from "./ops/log.js";
9
+ import { permits, permittedMutations } from "./permissions/policy.js";
10
+ import { nounOf } from "./schema/define-node.js";
11
+ import { PermissionDeniedError } from "./permissions/types.js";
12
+ import { checkUndo, undoPrimitives } from "./ops/undo.js";
13
+ import { tellTheWatchItsNames, tellTheWatchOfAStore, tellTheWatchOfAnAuthor, tellTheWatchOfARefusal } from "./watched.js";
14
+ const HUMAN = { kind: "human" };
15
+ /** A violation's identity across judgements: the rule, what it is about, and what it says. */
16
+ export function violationKey(v) {
17
+ return `${v.invariant}|${v.subjectId ?? ""}|${v.message}`;
18
+ }
19
+ /**
20
+ * The unit of the framework an app holds: a graph, the log it folds from,
21
+ * the mutations that may touch it and the invariants that judge it.
22
+ *
23
+ * Human and agent edits go through exactly the same path, which is why an
24
+ * agent's work needs no bespoke observability layer — it produces the same
25
+ * diff a human edit does, carrying the same attribution.
26
+ */
27
+ export class Store {
28
+ schema;
29
+ graph;
30
+ log;
31
+ mutations = new Map();
32
+ invariants;
33
+ invariantOptions;
34
+ policy;
35
+ /**
36
+ * The intelligence the app declared, so a surface can read the DOORS.
37
+ *
38
+ * The store already held the allowlist to enforce it; a seat that wants to
39
+ * offer "copy the prompt" or "send from here" according to what was
40
+ * declared needs the declaration itself, and the store is what every
41
+ * surface already has.
42
+ */
43
+ intelligence;
44
+ /** What each declared agent may do, by provider name. */
45
+ may = new Map();
46
+ /** What the enabled modules work out to; every surface reads this one answer. */
47
+ modules;
48
+ nextId;
49
+ now;
50
+ counter = 0;
51
+ listeners = new Set();
52
+ constructor(options) {
53
+ this.schema = options.schema;
54
+ this.invariants = options.invariants ?? [];
55
+ this.invariantOptions = options.invariantOptions ?? {};
56
+ this.policy = options.policy;
57
+ this.intelligence = options.intelligence ?? [];
58
+ for (const provider of options.intelligence ?? []) {
59
+ if (provider.may)
60
+ this.may.set(provider.name, new Set(provider.may));
61
+ }
62
+ this.modules = resolveModules(options.modules, options.enabledModules);
63
+ let n = 0;
64
+ this.nextId = options.ids ?? (() => `op${++n}`);
65
+ this.now = options.now ?? (() => new Date(0).toISOString());
66
+ for (const mutation of options.mutations ?? []) {
67
+ if (this.mutations.has(mutation.name)) {
68
+ throw new GraphError(`Duplicate mutation "${mutation.name}"`);
69
+ }
70
+ this.mutations.set(mutation.name, mutation);
71
+ }
72
+ /*
73
+ * THE DERIVED ACTS, registered like any other. A field you could set at
74
+ * creation, you can change: each kind with settable fields nobody writes
75
+ * gets `edit-<kind>`. What was made can be unmade: each kind gets
76
+ * `remove-<kind>`. Both titled, logged, undoable, judged by the
77
+ * invariants — and by the policy, through the acts that already write or
78
+ * create the kind (see `permits`). Nothing an app declares is replaced.
79
+ */
80
+ for (const mutation of deriveMutations(options.schema, options.mutations ?? [])) {
81
+ this.mutations.set(mutation.name, mutation);
82
+ }
83
+ tellTheWatchItsNames(options.schema, this.mutations.values(), options.policy);
84
+ if (options.log && options.snapshot) {
85
+ // Hydrate: the graph as stored, the history as recorded.
86
+ this.log = OperationLog.from(options.log);
87
+ this.graph = Graph.from(options.schema, options.snapshot, {
88
+ validate: options.validate ?? true,
89
+ });
90
+ }
91
+ else if (options.log) {
92
+ this.log = OperationLog.from(options.log);
93
+ this.graph = this.log.fold(options.schema, { validate: options.validate ?? true });
94
+ }
95
+ else {
96
+ this.log = new OperationLog();
97
+ this.graph = Graph.from(options.schema, options.snapshot ?? { nodes: [], edges: [] }, {
98
+ validate: options.validate ?? true,
99
+ });
100
+ }
101
+ /*
102
+ * A HYDRATED STORE COUNTS ON FROM WHERE THE LOG LEFT OFF.
103
+ *
104
+ * Both counters started at zero whatever history was handed in, so the
105
+ * first change after a reload was minted `op1` in `batch:1` — ids the
106
+ * log already held. `batches()` groups by batch id, so the new work was
107
+ * filed under the FIRST turn ever taken: the activity rail went on
108
+ * saying "Add a person" and never grew, undoing that turn would have
109
+ * taken the new change with it, and `log.get(id)` answered with the
110
+ * older op of the two. Every app that remembers anything did this on
111
+ * its second visit.
112
+ *
113
+ * Only the DEFAULT generators are wound forward; an app that supplies
114
+ * its own `ids` owns their uniqueness.
115
+ */
116
+ const trailing = (value) => {
117
+ const digits = /(\d+)$/.exec(value);
118
+ return digits ? Number(digits[1]) : 0;
119
+ };
120
+ for (const op of this.log.all()) {
121
+ this.counter = Math.max(this.counter, trailing(op.batch));
122
+ n = Math.max(n, trailing(op.id));
123
+ }
124
+ tellTheWatchOfAStore(this);
125
+ }
126
+ mutation(name) {
127
+ const found = this.mutations.get(name);
128
+ if (!found) {
129
+ throw new GraphError(`Unknown mutation "${name}"`, `Registered: ${[...this.mutations.keys()].join(", ") || "(none)"}`);
130
+ }
131
+ // A refusal is a result: the mutation exists, and this workspace has its
132
+ // module off — which is a different sentence from "unknown".
133
+ if (this.mutationDisabled(found)) {
134
+ throw new GraphError(`Mutation "${name}" belongs to a module this workspace has turned off`, `Enabled modules: ${[...this.modules.enabled].join(", ") || "(none)"}`);
135
+ }
136
+ return found;
137
+ }
138
+ /** Off by module, or a derived act of a kind the workspace has off. */
139
+ mutationDisabled(mutation) {
140
+ if (this.modules.disabledMutations.has(mutation.name))
141
+ return true;
142
+ return mutation.derived !== undefined && this.modules.disabledKinds.has(mutation.derived.kind);
143
+ }
144
+ allMutations() {
145
+ return [...this.mutations.values()].filter((mutation) => !this.mutationDisabled(mutation));
146
+ }
147
+ /** The declared acts a derived act resolves its permission through. */
148
+ viaOf(mutation) {
149
+ if (!mutation?.derived)
150
+ return undefined;
151
+ return derivedVia(this.schema, [...this.mutations.values()], mutation);
152
+ }
153
+ allInvariants() {
154
+ return this.invariants.filter((invariant) => this.invariantEnabled(invariant));
155
+ }
156
+ /**
157
+ * An invariant is off when its module is, and also when it is scoped to a
158
+ * kind whose module is — a rule about vehicles has nothing to judge in a
159
+ * workspace without them, and evaluating it anyway would resurrect the
160
+ * kind through its violations.
161
+ */
162
+ invariantEnabled(invariant) {
163
+ if (this.modules.disabledInvariants.has(invariant.name))
164
+ return false;
165
+ if (invariant.scope !== "graph") {
166
+ const kind = invariant.scope.kind;
167
+ if (this.modules.disabledKinds.has(kind))
168
+ return false;
169
+ }
170
+ return true;
171
+ }
172
+ /**
173
+ * Whether a principal may run a call, and — when not — who could.
174
+ *
175
+ * Public because an interface has to be able to ASK rather than guess: an
176
+ * action withheld by permission is stated rather than hidden, and stating
177
+ * it needs the same answer the enforcement uses. One source, two readings.
178
+ */
179
+ permits(call, principal = HUMAN) {
180
+ /*
181
+ * The same answer `apply` would give. A declared agent's `may` narrows
182
+ * it before the policy does; asked without it, the chat offered a
183
+ * repair as the starter seat, which may only add, and the press met
184
+ * "starter may not take-off here" (W-110).
185
+ */
186
+ const narrowed = this.refusesAgent(call, principal);
187
+ if (narrowed)
188
+ return { ok: false, refusal: narrowed };
189
+ return permits(this.policy, principal, call.name, this.subjectKindOf(call), this.viaOf(this.mutations.get(call.name)), this.subjectIdOf(call), this.words);
190
+ }
191
+ /** How the policy's refusals name an act and a kind: by title and noun, never by id. */
192
+ get words() {
193
+ return {
194
+ act: (name) => this.mutations.get(name)?.title,
195
+ noun: (kind) => nounOf(this.schema.tryDefinition(kind), kind),
196
+ };
197
+ }
198
+ /**
199
+ * Why a declared agent may not run this call, if it may not.
200
+ *
201
+ * Only ever narrows, and only for an agent: a human author has no
202
+ * allowlist, and a provider that declared no `may` may do whatever its
203
+ * roles allow, which is what "absent means all" has always meant.
204
+ */
205
+ refusesAgent(call, author) {
206
+ if (author.kind !== "agent" || author.id === undefined)
207
+ return undefined;
208
+ const allowed = this.may.get(author.id);
209
+ if (!allowed || allowed.has(call.name))
210
+ return undefined;
211
+ const titled = (name) => `“${this.mutations.get(name)?.title ?? name}”`;
212
+ const named = [...allowed].map(titled);
213
+ return {
214
+ mutation: call.name,
215
+ message: `${author.id} may not ${titled(call.name)} here: it was declared able to ${named.length === 0 ? "do nothing else" : named.join(", ")}.`,
216
+ wouldNeed: [],
217
+ };
218
+ }
219
+ /**
220
+ * Whether a principal may run ANY act of a module drawn only for those
221
+ * who administer it. This is the one question the interface asks before
222
+ * offering to show the installation's own districts; the answer comes
223
+ * from the same policy that refuses the acts.
224
+ */
225
+ mayAdminister(module, principal = HUMAN) {
226
+ const declared = this.modules.administered.get(module);
227
+ if (!declared)
228
+ return false;
229
+ return (declared.mutations ?? []).some((name) => {
230
+ const mutation = this.mutations.get(name);
231
+ if (!mutation)
232
+ return false;
233
+ const kinds = mutation.subject && mutation.subject.kinds !== "*"
234
+ ? mutation.subject.kinds
235
+ : [undefined];
236
+ return kinds.some((kind) => permits(this.policy, principal, name, kind, this.viaOf(mutation)).ok);
237
+ });
238
+ }
239
+ /** The kinds of administered modules this principal may not see at all. */
240
+ kindsKeptFrom(principal = HUMAN) {
241
+ const kept = new Set();
242
+ for (const [name, module] of this.modules.administered) {
243
+ if (this.mayAdminister(name, principal))
244
+ continue;
245
+ for (const kind of module.kinds ?? [])
246
+ kept.add(kind);
247
+ }
248
+ return kept;
249
+ }
250
+ subjectIdOf(call) {
251
+ const subject = this.mutations.get(call.name)?.subject;
252
+ if (!subject)
253
+ return undefined;
254
+ const id = call.args[subject.arg];
255
+ return typeof id === "string" ? id : undefined;
256
+ }
257
+ /**
258
+ * The mutations a principal may run at all.
259
+ *
260
+ * This is what narrows an agent seat's generated tool schema. The seat
261
+ * holds a principal; its tools are what that principal can do; there is no
262
+ * second list to keep in step.
263
+ */
264
+ permittedMutations(principal = HUMAN) {
265
+ const all = this.allMutations();
266
+ const declared = permittedMutations(this.policy, principal, all.filter((mutation) => !mutation.derived));
267
+ // A derived act is offered when the kind's own acts it rides are.
268
+ const derived = all.filter((mutation) => mutation.derived !== undefined &&
269
+ permits(this.policy, principal, mutation.name, mutation.derived.kind, this.viaOf(mutation)).ok);
270
+ return [...declared, ...derived];
271
+ }
272
+ /**
273
+ * The kind a call acts on, read off the mutation's own subject binding.
274
+ *
275
+ * Undefined when the mutation declares no subject, or when the argument
276
+ * names a node that is not there — an add, say. A grant restricted by kind
277
+ * refuses that case rather than allowing it: the safe reading of "I could
278
+ * not tell what this acts on" is no.
279
+ */
280
+ subjectKindOf(call) {
281
+ const definition = this.mutations.get(call.name);
282
+ const subject = definition?.subject;
283
+ if (!subject)
284
+ return undefined;
285
+ const id = call.args[subject.arg];
286
+ if (typeof id !== "string")
287
+ return undefined;
288
+ return this.graph.getNode(id)?.kind;
289
+ }
290
+ /** Current violations, evaluated fresh — nothing is cached or stale. */
291
+ violations(context) {
292
+ return evaluate(this.graph, this.allInvariants(), {
293
+ ...this.invariantOptions,
294
+ ...(context === undefined ? {} : { context }),
295
+ });
296
+ }
297
+ /**
298
+ * What a mutation would do, without doing it. Every action in the interface
299
+ * previews through here first — a typed mutation is checkable before it
300
+ * applies, whether a human or an agent proposed it.
301
+ */
302
+ /**
303
+ * Would this act change anything at all?
304
+ *
305
+ * Cheaper than `preview` — it compiles the mutation and counts what came
306
+ * out, without snapshotting the graph or re-evaluating the rules — because
307
+ * it is asked for every act on every selection. An act that compiles to no
308
+ * primitives leaves the graph exactly as it was: "Close it" on something
309
+ * already closed, an edge to a node that already has it, a guard inside
310
+ * the act that decided to do nothing. That is not an act, and offering it
311
+ * is a button that appears broken.
312
+ */
313
+ wouldChange(call) {
314
+ try {
315
+ return compileMutation(this.graph, this.mutation(call.name), call.args).primitives.length > 0;
316
+ }
317
+ catch {
318
+ // An act that cannot even compile is not one that changes nothing; let
319
+ // whoever asked find out the honest way.
320
+ return true;
321
+ }
322
+ }
323
+ preview(call, context) {
324
+ const definition = this.mutation(call.name);
325
+ const compiled = compileMutation(this.graph, definition, call.args);
326
+ return this.previewPrimitives(compiled.primitives, {
327
+ reads: compiled.reads,
328
+ writes: compiled.writes,
329
+ intent: compiled.intent,
330
+ context,
331
+ });
332
+ }
333
+ previewPrimitives(primitives, meta) {
334
+ const before = this.violations(meta.context);
335
+ const trial = Graph.from(this.schema, this.graph.snapshot());
336
+ const diff = trial.applyPrimitives(primitives);
337
+ const after = evaluate(trial, this.allInvariants(), {
338
+ ...this.invariantOptions,
339
+ ...(meta.context === undefined ? {} : { context: meta.context }),
340
+ });
341
+ const beforeKeys = new Set(before.map(violationKey));
342
+ const afterKeys = new Set(after.map(violationKey));
343
+ return {
344
+ diff,
345
+ primitives,
346
+ reads: meta.reads,
347
+ writes: meta.writes,
348
+ intent: meta.intent,
349
+ introduces: after.filter((v) => !beforeKeys.has(violationKey(v))),
350
+ resolves: before.filter((v) => !afterKeys.has(violationKey(v))),
351
+ violationsAfter: after,
352
+ };
353
+ }
354
+ /** Applies one mutation as its own batch, or joins an open batch. */
355
+ apply(call, options = {}) {
356
+ return this.applyAll([call], options);
357
+ }
358
+ /** Applies several mutations as one gesture — one batch, one undo unit. */
359
+ applyAll(calls, options = {}) {
360
+ const batch = options.batch ?? `batch:${++this.counter}`;
361
+ const author = options.author ?? HUMAN;
362
+ const ops = [];
363
+ const allPrimitives = [];
364
+ const reads = new Set();
365
+ const writes = new Set();
366
+ const intents = [];
367
+ const before = this.violations();
368
+ const rollback = this.graph.snapshot();
369
+ /*
370
+ * Permission is checked for the WHOLE gesture before any of it runs.
371
+ *
372
+ * A batch that applied three of five mutations and then refused the
373
+ * fourth would leave the graph in a state nobody asked for, and the
374
+ * rollback below is for errors rather than for policy. Refusing first is
375
+ * also the honest answer to "may I do this": the answer must not depend
376
+ * on how far through the list you got.
377
+ */
378
+ if (this.policy) {
379
+ for (const call of calls) {
380
+ const verdict = this.permits(call, author);
381
+ if (!verdict.ok) {
382
+ tellTheWatchOfARefusal(verdict.refusal, author.id);
383
+ throw new PermissionDeniedError(verdict.refusal);
384
+ }
385
+ }
386
+ }
387
+ /*
388
+ * AND AN AGENT IS HELD TO WHAT IT WAS DECLARED ABLE TO DO.
389
+ *
390
+ * Checked here rather than only in the tool runtime, because the runtime
391
+ * is one path and `store.apply` is the one everything else uses — an app
392
+ * applying a model's plan itself went straight past the allowlist, and
393
+ * every app would have had to remember to re-implement it. The roles a
394
+ * seat holds say what it may do at all; `may` narrows what THIS provider
395
+ * was brought in for, and the narrower of the two wins.
396
+ */
397
+ for (const call of calls) {
398
+ const refusal = this.refusesAgent(call, author);
399
+ if (refusal) {
400
+ tellTheWatchOfARefusal(refusal, author.id);
401
+ throw new PermissionDeniedError(refusal);
402
+ }
403
+ }
404
+ tellTheWatchOfAnAuthor(author.id);
405
+ try {
406
+ for (const call of calls) {
407
+ const definition = this.mutation(call.name);
408
+ const compiled = compileMutation(this.graph, definition, call.args);
409
+ /*
410
+ * AN ACT THAT DID NOTHING DOES NOT GO IN THE HISTORY.
411
+ *
412
+ * A mutation whose body decided to do nothing — the guard against a
413
+ * self-referential edge is the one every scaffolded app carries —
414
+ * compiles to no primitives. Logged anyway, it put a line in the
415
+ * activity rail saying a thing had happened, with an undo beside it
416
+ * that undid nothing, and wrote a record that replays to nothing on
417
+ * every future load. The call is still legal and still returns; it
418
+ * simply leaves no trace, because it left no trace.
419
+ */
420
+ if (compiled.primitives.length === 0)
421
+ continue;
422
+ const op = {
423
+ id: this.nextId(),
424
+ seq: this.log.length,
425
+ batch,
426
+ author,
427
+ intent: options.intent ?? compiled.intent,
428
+ mutation: call,
429
+ /*
430
+ * NORMALISED on the way into the record: a patch that clears a
431
+ * field says so with a value rather than with an absence, or the
432
+ * instruction is lost the moment the op is written to JSON and the
433
+ * inverse quietly does nothing. See `normalise` in primitives.ts.
434
+ */
435
+ primitives: compiled.primitives.map(normalise),
436
+ inverse: [...compiled.primitives].reverse().map(normalise).map(invert),
437
+ reads: compiled.reads,
438
+ writes: compiled.writes,
439
+ at: this.now(),
440
+ };
441
+ this.graph.applyPrimitives(op.primitives);
442
+ this.log.append(op);
443
+ ops.push(op);
444
+ allPrimitives.push(...op.primitives);
445
+ for (const id of op.reads)
446
+ reads.add(id);
447
+ for (const id of op.writes)
448
+ writes.add(id);
449
+ intents.push(op.intent);
450
+ }
451
+ }
452
+ catch (error) {
453
+ this.graph.load(rollback);
454
+ throw error;
455
+ }
456
+ const after = this.violations();
457
+ const beforeKeys = new Set(before.map(violationKey));
458
+ const afterKeys = new Set(after.map(violationKey));
459
+ const diff = diffSnapshots(rollback, this.graph.snapshot());
460
+ this.notify(diff, ops);
461
+ return {
462
+ batch,
463
+ ops,
464
+ diff,
465
+ primitives: allPrimitives,
466
+ reads: [...reads],
467
+ writes: [...writes],
468
+ intent: options.intent ?? intents.join("; "),
469
+ introduces: after.filter((v) => !beforeKeys.has(violationKey(v))),
470
+ resolves: before.filter((v) => !afterKeys.has(violationKey(v))),
471
+ violationsAfter: after,
472
+ };
473
+ }
474
+ /**
475
+ * OPERATIONS SOMEBODY ELSE ALREADY MADE, taken into this store.
476
+ *
477
+ * The one thing a store could not do, and the reason "where is my data"
478
+ * could only ever be answered with "in your browser": an op that a server
479
+ * has already judged and compiled has no business being judged and
480
+ * compiled again. Re-running it through `apply` would ask the policy
481
+ * about the wrong principal (whoever is at THIS keyboard, not whoever
482
+ * made the change), recompile against a graph that has moved, and mint a
483
+ * second op with a different id for the same event — so two browsers
484
+ * would diverge rather than converge.
485
+ *
486
+ * So: the primitives land, the op is appended with its own id, author and
487
+ * sequence intact, and the subscribers hear about it exactly as they hear
488
+ * about a local change. Undo still works on it, because an op carries its
489
+ * own inverse.
490
+ *
491
+ * Ops already in the log are skipped by id, so receiving the same batch
492
+ * twice — a poll that overlaps, a reconnect — is not a second change.
493
+ *
494
+ * `seq` is the position in THIS log and is renumbered on the way in; the
495
+ * op keeps everything that identifies it (its id, its author, its intent,
496
+ * its primitives and its inverse). A log is contiguous by construction —
497
+ * that is what makes undo and replay work — and an incoming op carries
498
+ * the sender's numbering, which cannot be this one's the moment this
499
+ * store has applied anything of its own. Whoever is tracking the sender's
500
+ * sequence must keep it themselves; `openRemote` does.
501
+ */
502
+ receive(ops, options = {}) {
503
+ const known = new Set(this.log.all().map((op) => op.id));
504
+ const fresh = ops.filter((op) => !known.has(op.id));
505
+ if (fresh.length === 0)
506
+ return [];
507
+ const before = this.graph.snapshot();
508
+ const landed = [];
509
+ for (const op of fresh) {
510
+ const here = { ...op, seq: this.log.all().length };
511
+ /*
512
+ * ALREADY IN EFFECT HERE. A client that applied a call provisionally
513
+ * and is now handed the server's op for it has the graph the op
514
+ * describes; applying the primitives again would add the node twice.
515
+ * The op still joins the log — it is the one everybody else has — so
516
+ * undo names it and the activity shows it, and the graph stays put.
517
+ */
518
+ if (!options.applied)
519
+ this.graph.applyPrimitives(here.primitives);
520
+ this.log.append(here);
521
+ landed.push(here);
522
+ }
523
+ this.notify(diffSnapshots(before, this.graph.snapshot()), landed);
524
+ return landed;
525
+ }
526
+ batches() {
527
+ return this.log.batches();
528
+ }
529
+ /** Whether a batch can come out on its own, and what blocks it if not. */
530
+ canUndo(batchIds) {
531
+ return checkUndo(this.log, typeof batchIds === "string" ? [batchIds] : batchIds);
532
+ }
533
+ /**
534
+ * What an undo would look like as a diff, including any invariant it would
535
+ * re-violate. Nothing applies until `undo` is called.
536
+ */
537
+ previewUndo(batchIds, context) {
538
+ const check = this.canUndo(batchIds);
539
+ if (!check.ok)
540
+ return { ok: false, check };
541
+ const primitives = undoPrimitives(check.ops);
542
+ const writes = new Set();
543
+ for (const op of check.ops)
544
+ for (const id of op.writes)
545
+ writes.add(id);
546
+ return {
547
+ ok: true,
548
+ check,
549
+ ...this.previewPrimitives(primitives, {
550
+ reads: [...new Set(check.ops.flatMap((op) => [...op.reads]))],
551
+ writes: [...writes],
552
+ intent: `Undo: ${check.ops.map((op) => op.intent).join("; ")}`,
553
+ ...(context === undefined ? {} : { context }),
554
+ }),
555
+ };
556
+ }
557
+ /**
558
+ * Undo APPENDS inverse ops rather than rewinding a pointer. History is
559
+ * never destroyed, redo is simply the undo of an undo, and dropping an
560
+ * agent's turn while keeping your own edits is a dependency check rather
561
+ * than a stack pop.
562
+ */
563
+ undo(batchIds, options = {}) {
564
+ const ids = typeof batchIds === "string" ? [batchIds] : batchIds;
565
+ const check = this.canUndo(ids);
566
+ if (!check.ok)
567
+ throw new GraphError(check.message);
568
+ const batch = options.batch ?? `undo:${++this.counter}`;
569
+ const author = options.author ?? HUMAN;
570
+ /*
571
+ * Undo is a CHANGE, and it is judged like one.
572
+ *
573
+ * Without this, undo is the way around the policy: a principal who may
574
+ * not reassign a run could take back somebody else's reassignment and
575
+ * arrive at exactly the state they were refused. What you may undo is
576
+ * what you may have done — the mutation each operation ran, judged
577
+ * again now.
578
+ */
579
+ if (this.policy) {
580
+ for (const target of check.ops) {
581
+ if (!target.mutation)
582
+ continue;
583
+ const verdict = this.permits(target.mutation, author);
584
+ if (!verdict.ok) {
585
+ const refusal = {
586
+ ...verdict.refusal,
587
+ message: `Not permitted to undo "${target.intent}": ${verdict.refusal.message}`,
588
+ };
589
+ tellTheWatchOfARefusal(refusal, author.id);
590
+ throw new PermissionDeniedError(refusal);
591
+ }
592
+ }
593
+ }
594
+ const rollback = this.graph.snapshot();
595
+ const before = this.violations();
596
+ const ops = [];
597
+ for (const target of [...check.ops].sort((a, b) => b.seq - a.seq)) {
598
+ const op = {
599
+ id: this.nextId(),
600
+ seq: this.log.length,
601
+ batch,
602
+ author,
603
+ intent: options.intent ?? `Undo: ${target.intent}`,
604
+ mutation: null,
605
+ primitives: target.inverse,
606
+ inverse: [...target.inverse].reverse().map(invert),
607
+ reads: target.reads,
608
+ writes: target.writes,
609
+ at: this.now(),
610
+ undoes: target.id,
611
+ };
612
+ this.graph.applyPrimitives(op.primitives);
613
+ this.log.append(op);
614
+ ops.push(op);
615
+ }
616
+ const after = this.violations();
617
+ const beforeKeys = new Set(before.map(violationKey));
618
+ const afterKeys = new Set(after.map(violationKey));
619
+ const diff = diffSnapshots(rollback, this.graph.snapshot());
620
+ this.notify(diff, ops);
621
+ return {
622
+ batch,
623
+ ops,
624
+ diff,
625
+ primitives: ops.flatMap((op) => [...op.primitives]),
626
+ reads: [...new Set(ops.flatMap((op) => [...op.reads]))],
627
+ writes: [...new Set(ops.flatMap((op) => [...op.writes]))],
628
+ intent: options.intent ?? ops.map((op) => op.intent).join("; "),
629
+ introduces: after.filter((v) => !beforeKeys.has(violationKey(v))),
630
+ resolves: before.filter((v) => !afterKeys.has(violationKey(v))),
631
+ violationsAfter: after,
632
+ };
633
+ }
634
+ /** Redo is the undo of an undo — no separate stack exists. */
635
+ redo(undoBatchId, options = {}) {
636
+ return this.undo(undoBatchId, options);
637
+ }
638
+ /**
639
+ * Every applied change, with the operations that caused it.
640
+ *
641
+ * Notified AFTER the log is appended, not during the graph write: a
642
+ * subscriber that reads the log to find out who made a change must not see
643
+ * a log that has not caught up yet. Subscribing to the graph directly
644
+ * gives the diff without that guarantee.
645
+ */
646
+ subscribe(listener) {
647
+ this.listeners.add(listener);
648
+ return () => this.listeners.delete(listener);
649
+ }
650
+ notify(diff, ops) {
651
+ if (ops.length === 0)
652
+ return;
653
+ for (const listener of this.listeners)
654
+ listener(diff, ops);
655
+ }
656
+ snapshot() {
657
+ return this.graph.snapshot();
658
+ }
659
+ }
660
+ //# sourceMappingURL=store.js.map