yarramate 0.23.0 → 1.0.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 (159) hide show
  1. package/README.md +13 -4
  2. package/catalogues/core-enrichment.yaml +79 -55
  3. package/dist/adapter-mapping.js +6 -1
  4. package/dist/adapters/likec4-cli.js +36 -7
  5. package/dist/adapters/visual/client.d.ts +2 -2
  6. package/dist/adapters/visual/client.js +79 -10
  7. package/dist/adapters/visual/protocol-contract.d.ts +124 -10
  8. package/dist/adapters/visual/protocol-contract.js +12 -1
  9. package/dist/adapters/visual/protocol.d.ts +63 -0
  10. package/dist/adapters/visual/protocol.js +96 -5
  11. package/dist/adapters/visual/session-server.d.ts +1 -6
  12. package/dist/adapters/visual/session-server.js +243 -143
  13. package/dist/adapters/visual/session-store.d.ts +9 -0
  14. package/dist/adapters/visual/session-store.js +14 -5
  15. package/dist/adapters/visual/view-identity.d.ts +135 -0
  16. package/dist/adapters/visual/view-identity.js +200 -0
  17. package/dist/adapters/visual/wire.d.ts +19 -4
  18. package/dist/adapters/visual/workspace-model.d.ts +116 -0
  19. package/dist/adapters/visual/workspace-model.js +212 -0
  20. package/dist/adapters/visual-cli.d.ts +1 -1
  21. package/dist/adapters/visual-cli.js +30 -21
  22. package/dist/apply-cli.d.ts +2 -0
  23. package/dist/apply-cli.js +83 -0
  24. package/dist/apply-command.d.ts +84 -3
  25. package/dist/apply-command.js +218 -109
  26. package/dist/archimate-relationships.generated.d.ts +22 -0
  27. package/dist/archimate-relationships.generated.js +156 -0
  28. package/dist/ask-command.js +18 -6
  29. package/dist/attestation-staleness.js +2 -2
  30. package/dist/changed.js +1 -1
  31. package/dist/check-command.js +13 -5
  32. package/dist/cli-support.d.ts +1 -1
  33. package/dist/cli-support.js +1 -1
  34. package/dist/cli.js +1 -1
  35. package/dist/compiler.d.ts +31 -3
  36. package/dist/compiler.js +321 -81
  37. package/dist/concept-drafting.d.ts +27 -0
  38. package/dist/concept-drafting.js +75 -0
  39. package/dist/core-contract.js +6 -1
  40. package/dist/deletion-drafting.d.ts +52 -0
  41. package/dist/deletion-drafting.js +100 -0
  42. package/dist/evidence.js +6 -1
  43. package/dist/graph-projection.d.ts +14 -0
  44. package/dist/graph-projection.js +4 -0
  45. package/dist/index.d.ts +8 -1
  46. package/dist/index.js +8 -1
  47. package/dist/interrogate-command.js +63 -17
  48. package/dist/kind-label.d.ts +4 -3
  49. package/dist/kind-label.js +4 -3
  50. package/dist/nesting.d.ts +15 -0
  51. package/dist/nesting.js +2 -0
  52. package/dist/notation/archimate.d.ts +1 -1
  53. package/dist/notation/archimate.js +108 -9
  54. package/dist/profile.d.ts +9 -12
  55. package/dist/profile.js +17 -26
  56. package/dist/projection.d.ts +62 -3
  57. package/dist/projection.js +127 -43
  58. package/dist/relationship-drafting.d.ts +51 -0
  59. package/dist/relationship-drafting.js +92 -0
  60. package/dist/relationship-matrix.d.ts +14 -0
  61. package/dist/relationship-matrix.js +74 -0
  62. package/dist/shipped-profile.d.ts +1 -1
  63. package/dist/shipped-profile.js +19 -5
  64. package/dist/source-store.d.ts +95 -0
  65. package/dist/source-store.js +170 -0
  66. package/dist/subject-references.js +1 -1
  67. package/dist/visual-app/assets/index-BLcpI8WN.js +394 -0
  68. package/dist/visual-app/assets/index-DLutIWES.css +1 -0
  69. package/dist/visual-app/index.html +2 -2
  70. package/dist/visual-app-lib/editor.d.ts +1 -0
  71. package/dist/visual-app-lib/editor.js +90968 -0
  72. package/dist/visual-app-lib/styles.css +2 -0
  73. package/dist/visual-app-lib/types/adapter-mapping.d.ts +40 -0
  74. package/dist/visual-app-lib/types/adapters/visual/protocol-contract.d.ts +455 -0
  75. package/dist/visual-app-lib/types/adapters/visual/view-identity.d.ts +135 -0
  76. package/dist/visual-app-lib/types/adapters/visual/wire.d.ts +141 -0
  77. package/dist/visual-app-lib/types/adapters/visual/workspace-model.d.ts +116 -0
  78. package/dist/visual-app-lib/types/apply-command.d.ts +93 -0
  79. package/dist/visual-app-lib/types/archimate-relationships.generated.d.ts +22 -0
  80. package/dist/visual-app-lib/types/brief.d.ts +5 -0
  81. package/dist/visual-app-lib/types/compiler.d.ts +155 -0
  82. package/dist/visual-app-lib/types/concept-drafting.d.ts +27 -0
  83. package/dist/visual-app-lib/types/deletion-drafting.d.ts +52 -0
  84. package/dist/visual-app-lib/types/evidence.d.ts +62 -0
  85. package/dist/visual-app-lib/types/graph-claims.d.ts +18 -0
  86. package/dist/visual-app-lib/types/graph-projection.d.ts +77 -0
  87. package/dist/visual-app-lib/types/kind-label.d.ts +9 -0
  88. package/dist/visual-app-lib/types/nesting.d.ts +15 -0
  89. package/dist/visual-app-lib/types/notation/archimate.d.ts +71 -0
  90. package/dist/visual-app-lib/types/operations.d.ts +157 -0
  91. package/dist/visual-app-lib/types/profile.d.ts +47 -0
  92. package/dist/visual-app-lib/types/projection.d.ts +113 -0
  93. package/dist/visual-app-lib/types/relationship-drafting.d.ts +51 -0
  94. package/dist/visual-app-lib/types/relationship-matrix.d.ts +14 -0
  95. package/dist/visual-app-lib/types/shipped-profile.d.ts +2 -0
  96. package/dist/visual-app-lib/types/source-document.d.ts +36 -0
  97. package/dist/visual-app-lib/types/source-store.d.ts +95 -0
  98. package/dist/visual-app-lib/types/subject-references.d.ts +92 -0
  99. package/dist/visual-app-lib/types/visual-app/App.d.ts +15 -0
  100. package/dist/visual-app-lib/types/visual-app/badges.d.ts +7 -0
  101. package/dist/visual-app-lib/types/visual-app/changeset-tray.d.ts +106 -0
  102. package/dist/visual-app-lib/types/visual-app/confirm-dialog.d.ts +14 -0
  103. package/dist/visual-app-lib/types/visual-app/connection-panel.d.ts +23 -0
  104. package/dist/visual-app-lib/types/visual-app/context-menu-model.d.ts +187 -0
  105. package/dist/visual-app-lib/types/visual-app/context-menu.d.ts +24 -0
  106. package/dist/visual-app-lib/types/visual-app/describe-query.d.ts +9 -0
  107. package/dist/visual-app-lib/types/visual-app/editor-host.d.ts +70 -0
  108. package/dist/visual-app-lib/types/visual-app/faults.d.ts +26 -0
  109. package/dist/visual-app-lib/types/visual-app/graph-canvas.d.ts +73 -0
  110. package/dist/visual-app-lib/types/visual-app/kind-icons.d.ts +3 -0
  111. package/dist/visual-app-lib/types/visual-app/local-host.d.ts +38 -0
  112. package/dist/visual-app-lib/types/visual-app/mount.d.ts +56 -0
  113. package/dist/visual-app-lib/types/visual-app/prompt-dialog.d.ts +24 -0
  114. package/dist/visual-app-lib/types/visual-app/query-fields.d.ts +69 -0
  115. package/dist/visual-app-lib/types/visual-app/query-panel.d.ts +142 -0
  116. package/dist/visual-app-lib/types/visual-app/quick-filter.d.ts +11 -0
  117. package/dist/visual-app-lib/types/visual-app/relationship-kind-options.d.ts +50 -0
  118. package/dist/visual-app-lib/types/visual-app/save-view.d.ts +86 -0
  119. package/dist/visual-app-lib/types/visual-app/section-stack.d.ts +59 -0
  120. package/dist/visual-app-lib/types/visual-app/session-client.d.ts +38 -0
  121. package/dist/visual-app-lib/types/visual-app/state.d.ts +328 -0
  122. package/dist/visual-app-lib/types/visual-app/subject-draft-panel.d.ts +22 -0
  123. package/dist/visual-app-lib/types/visual-app/subject-form.d.ts +42 -0
  124. package/dist/visual-app-lib/types/visual-app/view-tree-model.d.ts +122 -0
  125. package/dist/visual-app-lib/types/visual-app/view-tree.d.ts +32 -0
  126. package/dist/visual-app-lib/types/visual-app/workspace-state.d.ts +268 -0
  127. package/dist/visual-app-lib/types/workspace.d.ts +29 -0
  128. package/dist/workspace.js +7 -1
  129. package/docs/CONSUMING-YARRAMATE.md +51 -2
  130. package/package.json +9 -2
  131. package/schema/yarramate-adapter-mapping.schema.json +1 -1
  132. package/schema/yarramate-ask-result.schema.json +24 -3
  133. package/schema/yarramate-check-result.schema.json +9 -0
  134. package/schema/yarramate-diagnostic-result.schema.json +9 -0
  135. package/schema/yarramate-document.schema.json +6 -0
  136. package/schema/yarramate-evidence-report.schema.json +1 -1
  137. package/schema/yarramate-evidence.schema.json +1 -1
  138. package/schema/yarramate-graph-v2.schema.json +34 -9
  139. package/schema/yarramate-likec4-generated-project-v2.schema.json +1 -1
  140. package/schema/yarramate-likec4-generated-project.schema.json +1 -1
  141. package/schema/yarramate-likec4-project.schema.json +1 -1
  142. package/schema/yarramate-projection-result.schema.json +3 -7
  143. package/schema/yarramate-projection.schema.json +16 -16
  144. package/schema/yarramate-reconciliation-report.schema.json +2 -2
  145. package/schema/yarramate-rtm.schema.json +1 -1
  146. package/schema/yarramate-state-comparison.schema.json +1 -1
  147. package/schema/yarramate-visual-diagnostic-result.schema.json +9 -0
  148. package/schema/yarramate-visual-event.schema.json +40 -41
  149. package/schema/yarramate-visual-graph.schema.json +8 -0
  150. package/schema/yarramate-visual-handoff.schema.json +3 -3
  151. package/schema/yarramate-visual-session-descriptor.schema.json +6 -6
  152. package/schema/yarramate-visual-session-started.schema.json +10 -9
  153. package/schema/yarramate-visual-status.schema.json +3 -3
  154. package/skills/yarramate-architecture/SKILL.md +3 -3
  155. package/skills/yarramate-architecture/references/modelling-patterns.md +23 -44
  156. package/skills/yarramate-architecture/references/native-authoring.md +32 -49
  157. package/skills/yarramate-architecture/references/visual-conversations.md +13 -10
  158. package/dist/visual-app/assets/index-CRd1Khma.js +0 -354
  159. package/dist/visual-app/assets/index-tZSHlU-a.css +0 -1
@@ -0,0 +1,135 @@
1
+ import type { ProjectionDefinition, ProjectionQuery } from "../../projection.js";
2
+ /**
3
+ * How a saved view gets its id, its path, and its document.
4
+ *
5
+ * The browser composes a `write-view` operation now rather than asking the
6
+ * server to compose one at commit time (ADR 0103): a staged row has to name
7
+ * the document it will write before it is committed, because that is what the
8
+ * reviewer reads in the tray and what the digest pins against.
9
+ *
10
+ * So this lives where both sides can reach it. No React, no cytoscape, and
11
+ * nothing from `node:` — the browser bundle imports it for its value, and
12
+ * `visual-app-browser-safety.test.ts` is what holds that honest. Validation
13
+ * stays on the server, where the schema and its loader are: composing a
14
+ * projection is arithmetic, deciding it is acceptable is not.
15
+ */
16
+ /** Where projections live when nothing says otherwise. */
17
+ export declare const DEFAULT_PROJECTION_DIRECTORY = ".yarramate/projections";
18
+ /**
19
+ * Turns a view title into a schema-valid projection id: lowercase,
20
+ * hyphen-separated, letter-led. A title with no letters or digits degrades
21
+ * to "view" rather than producing an id the schema would reject.
22
+ */
23
+ export declare const slugify: (title: string) => string;
24
+ /** Appends a numeric suffix only when the base id collides with a known view. */
25
+ export declare const uniqueViewId: (base: string, taken: ReadonlySet<string>) => string;
26
+ /** The id a title should take, given the ids already in use. */
27
+ export declare const viewIdFrom: (title: string, taken: ReadonlySet<string>) => string;
28
+ /**
29
+ * Where a view's document goes: beside every other one.
30
+ *
31
+ * The directory no longer carries meaning. Folders are DECLARED in the
32
+ * projection now (ADR 0104), so a view files itself with a label and the path
33
+ * is only an address - which is what makes "New folder" a thing the editor can
34
+ * do at all. Writing into a subdirectory was the one motion that could put a
35
+ * projection where the manifest's patterns do not reach (`YMVS315`), and the
36
+ * editor no longer has a reason to try.
37
+ */
38
+ export declare const projectionPathFor: (id: string) => string;
39
+ /**
40
+ * The projection document a saved view is, composed from what the form holds.
41
+ *
42
+ * `title` and `description` are presentation fields rather than top-level
43
+ * ones, which is where `ProjectionDefinition` puts them, and every other
44
+ * presentation field the active view declared is carried through — dropping
45
+ * one here is how overwriting a view silently loses its nesting or direction.
46
+ */
47
+ /** The folder a saved view declares, or `""` for none. */
48
+ export declare const declaredFolder: (view: {
49
+ readonly presentation: ProjectionDefinition["presentation"];
50
+ }) => string;
51
+ /**
52
+ * Retitling a view, as a staged write.
53
+ *
54
+ * The path and the id do NOT move. A projection's id decides its filename and
55
+ * also keys its layout sidecar (`.yarramate/visual-layout/<id>.yaml`), so a
56
+ * rename that carried the id along would silently orphan the positions the
57
+ * reviewer dragged. Renaming is a change to what the view is called; moving it
58
+ * is a different motion, and not one #246 asked for.
59
+ *
60
+ * Every other presentation field the view declared is carried through by
61
+ * `composeProjection`, so a rename cannot quietly drop a nesting vocabulary or
62
+ * a direction the canvas never showed.
63
+ */
64
+ export declare const renameView: (view: SavedView, title: string) => {
65
+ readonly path: string;
66
+ readonly projection: ProjectionDefinition;
67
+ };
68
+ /**
69
+ * Copying a view into a new document beside it.
70
+ *
71
+ * The copy keeps the original's folder, because a duplicate the reviewer then
72
+ * has to move is a duplicate in the wrong place - and it comes along for free
73
+ * now that a folder is a declared field `composeProjection` carries with every
74
+ * other one. It does NOT inherit the layout sidecar: that is keyed by id, and
75
+ * a copy is a different view that lays itself out.
76
+ */
77
+ export declare const duplicateView: (view: SavedView, taken: ReadonlySet<string>) => {
78
+ readonly path: string;
79
+ readonly projection: ProjectionDefinition;
80
+ };
81
+ /** What these need of a saved view, and nothing about how it is drawn. */
82
+ export interface SavedView {
83
+ readonly id: string;
84
+ readonly title: string;
85
+ readonly description: string;
86
+ readonly query: ProjectionQuery;
87
+ readonly presentation: ProjectionDefinition["presentation"];
88
+ readonly path: string;
89
+ }
90
+ export declare const composeProjection: (input: {
91
+ readonly id: string;
92
+ readonly title: string;
93
+ readonly description: string;
94
+ readonly query: ProjectionQuery;
95
+ readonly presentation: ProjectionDefinition["presentation"];
96
+ }) => ProjectionDefinition;
97
+ /**
98
+ * Whether a view can be told which subjects it holds.
99
+ *
100
+ * Only a view that ENUMERATES `subjects:` can. A view that describes its
101
+ * subjects with facets — a layer, a kind, a state — already includes anything
102
+ * matching them and excludes anything that does not, so there is nothing for a
103
+ * membership edit to say to it: the subject is in or out by what it is, and
104
+ * saying otherwise means editing the query rather than a list (#255).
105
+ */
106
+ export declare const enumeratesSubjects: (query: ProjectionQuery) => query is ProjectionQuery & {
107
+ readonly subjects: readonly string[];
108
+ };
109
+ /**
110
+ * The same view, holding one more subject or one fewer.
111
+ *
112
+ * `null` when there is nothing to stage: the view describes its subjects
113
+ * rather than listing them, or the list already says what was asked for.
114
+ * Returning the absence rather than an unchanged document is what keeps a
115
+ * no-op out of the changeset, where a row that writes a file back exactly as
116
+ * it was is a row the reviewer has to read and discard for nothing.
117
+ */
118
+ export declare const withMembership: (projection: ProjectionDefinition, subjectId: string, membership: "add" | "remove") => ProjectionDefinition | null;
119
+ /**
120
+ * What a membership edit did to a view, as the tray reads it: `+id` for a
121
+ * subject this row adds, `-id` for one it drops, against the document the
122
+ * workspace holds. Empty when the row changed something else — a title, a
123
+ * query, a presentation flag — which the row says by other means.
124
+ */
125
+ export declare const membershipDelta: (saved: ProjectionQuery, staged: ProjectionQuery) => readonly string[];
126
+ /**
127
+ * Two projection documents, compared by what they SAY rather than by how they
128
+ * are written.
129
+ *
130
+ * Key order is the author's: a document read back off YAML holds its fields in
131
+ * the order someone wrote them, and one composed here holds them in the order
132
+ * this file writes them. Comparing the two as text reports every view as
133
+ * changed the moment anything touches it.
134
+ */
135
+ export declare const sameDocument: (left: ProjectionDefinition, right: ProjectionDefinition) => boolean;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * How a saved view gets its id, its path, and its document.
3
+ *
4
+ * The browser composes a `write-view` operation now rather than asking the
5
+ * server to compose one at commit time (ADR 0103): a staged row has to name
6
+ * the document it will write before it is committed, because that is what the
7
+ * reviewer reads in the tray and what the digest pins against.
8
+ *
9
+ * So this lives where both sides can reach it. No React, no cytoscape, and
10
+ * nothing from `node:` — the browser bundle imports it for its value, and
11
+ * `visual-app-browser-safety.test.ts` is what holds that honest. Validation
12
+ * stays on the server, where the schema and its loader are: composing a
13
+ * projection is arithmetic, deciding it is acceptable is not.
14
+ */
15
+ /** Where projections live when nothing says otherwise. */
16
+ export const DEFAULT_PROJECTION_DIRECTORY = ".yarramate/projections";
17
+ /**
18
+ * Turns a view title into a schema-valid projection id: lowercase,
19
+ * hyphen-separated, letter-led. A title with no letters or digits degrades
20
+ * to "view" rather than producing an id the schema would reject.
21
+ */
22
+ export const slugify = (title) => {
23
+ const cleaned = title
24
+ .toLowerCase()
25
+ .replace(/[^a-z0-9]+/g, "-")
26
+ .replace(/^-+|-+$/g, "");
27
+ if (cleaned.length === 0)
28
+ return "view";
29
+ return /^[a-z]/.test(cleaned) ? cleaned : `view-${cleaned}`;
30
+ };
31
+ /** Appends a numeric suffix only when the base id collides with a known view. */
32
+ export const uniqueViewId = (base, taken) => {
33
+ if (!taken.has(base))
34
+ return base;
35
+ let suffix = 2;
36
+ while (taken.has(`${base}-${suffix}`))
37
+ suffix += 1;
38
+ return `${base}-${suffix}`;
39
+ };
40
+ /** The id a title should take, given the ids already in use. */
41
+ export const viewIdFrom = (title, taken) => uniqueViewId(slugify(title), taken);
42
+ /**
43
+ * Where a view's document goes: beside every other one.
44
+ *
45
+ * The directory no longer carries meaning. Folders are DECLARED in the
46
+ * projection now (ADR 0104), so a view files itself with a label and the path
47
+ * is only an address - which is what makes "New folder" a thing the editor can
48
+ * do at all. Writing into a subdirectory was the one motion that could put a
49
+ * projection where the manifest's patterns do not reach (`YMVS315`), and the
50
+ * editor no longer has a reason to try.
51
+ */
52
+ export const projectionPathFor = (id) => `${DEFAULT_PROJECTION_DIRECTORY}/${id}.yaml`;
53
+ /**
54
+ * The projection document a saved view is, composed from what the form holds.
55
+ *
56
+ * `title` and `description` are presentation fields rather than top-level
57
+ * ones, which is where `ProjectionDefinition` puts them, and every other
58
+ * presentation field the active view declared is carried through — dropping
59
+ * one here is how overwriting a view silently loses its nesting or direction.
60
+ */
61
+ /** The folder a saved view declares, or `""` for none. */
62
+ export const declaredFolder = (view) => view.presentation?.folder ?? "";
63
+ /**
64
+ * Retitling a view, as a staged write.
65
+ *
66
+ * The path and the id do NOT move. A projection's id decides its filename and
67
+ * also keys its layout sidecar (`.yarramate/visual-layout/<id>.yaml`), so a
68
+ * rename that carried the id along would silently orphan the positions the
69
+ * reviewer dragged. Renaming is a change to what the view is called; moving it
70
+ * is a different motion, and not one #246 asked for.
71
+ *
72
+ * Every other presentation field the view declared is carried through by
73
+ * `composeProjection`, so a rename cannot quietly drop a nesting vocabulary or
74
+ * a direction the canvas never showed.
75
+ */
76
+ export const renameView = (view, title) => ({
77
+ path: view.path,
78
+ projection: composeProjection({
79
+ id: view.id,
80
+ title,
81
+ description: view.description,
82
+ query: view.query,
83
+ presentation: view.presentation,
84
+ }),
85
+ });
86
+ /**
87
+ * Copying a view into a new document beside it.
88
+ *
89
+ * The copy keeps the original's folder, because a duplicate the reviewer then
90
+ * has to move is a duplicate in the wrong place - and it comes along for free
91
+ * now that a folder is a declared field `composeProjection` carries with every
92
+ * other one. It does NOT inherit the layout sidecar: that is keyed by id, and
93
+ * a copy is a different view that lays itself out.
94
+ */
95
+ export const duplicateView = (view, taken) => {
96
+ const title = `${view.title} copy`;
97
+ const id = viewIdFrom(title, taken);
98
+ return {
99
+ path: projectionPathFor(id),
100
+ projection: composeProjection({
101
+ id,
102
+ title,
103
+ description: view.description,
104
+ query: view.query,
105
+ presentation: view.presentation,
106
+ }),
107
+ };
108
+ };
109
+ export const composeProjection = (input) => ({
110
+ format: "yarramate/projection/v1",
111
+ id: input.id,
112
+ version: "1.0",
113
+ query: input.query,
114
+ presentation: {
115
+ ...(input.presentation ?? {}),
116
+ title: input.title,
117
+ description: input.description,
118
+ },
119
+ });
120
+ /**
121
+ * Whether a view can be told which subjects it holds.
122
+ *
123
+ * Only a view that ENUMERATES `subjects:` can. A view that describes its
124
+ * subjects with facets — a layer, a kind, a state — already includes anything
125
+ * matching them and excludes anything that does not, so there is nothing for a
126
+ * membership edit to say to it: the subject is in or out by what it is, and
127
+ * saying otherwise means editing the query rather than a list (#255).
128
+ */
129
+ export const enumeratesSubjects = (query) => query.subjects !== undefined;
130
+ /**
131
+ * The same view, holding one more subject or one fewer.
132
+ *
133
+ * `null` when there is nothing to stage: the view describes its subjects
134
+ * rather than listing them, or the list already says what was asked for.
135
+ * Returning the absence rather than an unchanged document is what keeps a
136
+ * no-op out of the changeset, where a row that writes a file back exactly as
137
+ * it was is a row the reviewer has to read and discard for nothing.
138
+ */
139
+ export const withMembership = (projection, subjectId, membership) => {
140
+ if (!enumeratesSubjects(projection.query))
141
+ return null;
142
+ const subjects = projection.query.subjects;
143
+ const holds = subjects.includes(subjectId);
144
+ if (membership === "add" ? holds : !holds)
145
+ return null;
146
+ return {
147
+ ...projection,
148
+ query: {
149
+ ...projection.query,
150
+ subjects: membership === "add"
151
+ ? // Appended rather than sorted in: the list is the author's, and
152
+ // reordering it would rewrite lines nobody touched.
153
+ [...subjects, subjectId]
154
+ : subjects.filter((id) => id !== subjectId),
155
+ },
156
+ };
157
+ };
158
+ /**
159
+ * What a membership edit did to a view, as the tray reads it: `+id` for a
160
+ * subject this row adds, `-id` for one it drops, against the document the
161
+ * workspace holds. Empty when the row changed something else — a title, a
162
+ * query, a presentation flag — which the row says by other means.
163
+ */
164
+ export const membershipDelta = (saved, staged) => {
165
+ if (!enumeratesSubjects(saved) || !enumeratesSubjects(staged))
166
+ return [];
167
+ return [
168
+ ...staged.subjects
169
+ .filter((id) => !saved.subjects.includes(id))
170
+ .map((id) => `+${id}`),
171
+ ...saved.subjects
172
+ .filter((id) => !staged.subjects.includes(id))
173
+ .map((id) => `-${id}`),
174
+ ];
175
+ };
176
+ /**
177
+ * Two projection documents, compared by what they SAY rather than by how they
178
+ * are written.
179
+ *
180
+ * Key order is the author's: a document read back off YAML holds its fields in
181
+ * the order someone wrote them, and one composed here holds them in the order
182
+ * this file writes them. Comparing the two as text reports every view as
183
+ * changed the moment anything touches it.
184
+ */
185
+ export const sameDocument = (left, right) => canonical(left) === canonical(right);
186
+ const canonical = (value) => {
187
+ if (Array.isArray(value)) {
188
+ // Order is meaningful inside a list - a subjects list is the author's own
189
+ // ordering - so only object keys are sorted.
190
+ return `[${value.map(canonical).join(",")}]`;
191
+ }
192
+ if (value !== null && typeof value === "object") {
193
+ return `{${Object.entries(value)
194
+ .filter(([, entry]) => entry !== undefined)
195
+ .sort(([a], [b]) => (a < b ? -1 : 1))
196
+ .map(([key, entry]) => `${JSON.stringify(key)}:${canonical(entry)}`)
197
+ .join(",")}}`;
198
+ }
199
+ return JSON.stringify(value) ?? "null";
200
+ };
@@ -1,5 +1,5 @@
1
1
  import type { CanvasGraph } from '../../graph-projection.js';
2
- import type { VISUAL_PROTOCOL_VERSION, VisualApplyResultPayload, VisualAuthority, VisualBrowserInput, VisualCapabilities, VisualChoicePresentPayload, VisualDiagnostic, VisualFilterResultPayload, VisualFreezeReason, VisualKindOption, VisualLayoutPositions, VisualLayoutSaveResultPayload, VisualResponse, VisualTerminationReason, VisualViewSaveResultPayload, VisualViewSummary } from './protocol-contract.js';
2
+ import type { VISUAL_PROTOCOL_VERSION, VisualApplyResultPayload, VisualAuthority, VisualBrowserInput, VisualCapabilities, VisualChoicePresentPayload, VisualDiagnostic, VisualFilterResultPayload, VisualFreezeReason, VisualKindOption, VisualLayoutPositions, VisualLayoutSaveResultPayload, VisualResponse, VisualTerminationReason, VisualViewSummary } from './protocol-contract.js';
3
3
  /**
4
4
  * Transport shapes the session server and the browser application both speak.
5
5
  *
@@ -32,6 +32,17 @@ export interface VisualRenderedModel {
32
32
  * can state what it rendered when it asks for a commit.
33
33
  */
34
34
  readonly sourceDigests: Readonly<Record<string, string>>;
35
+ /**
36
+ * The sha256 of every projection this session knows about, keyed by
37
+ * manifest-relative path.
38
+ *
39
+ * Kept apart from `sourceDigests` rather than folded into it: that map means
40
+ * what the GRAPH was compiled from, which `YMVS112` requires a canonical
41
+ * model to state, and a projection is not part of it. A staged view
42
+ * operation pins against this one instead (ADR 0103), and a path absent from
43
+ * it is a projection the commit will create.
44
+ */
45
+ readonly projectionDigests: Readonly<Record<string, string>>;
35
46
  }
36
47
  /**
37
48
  * One line of the conversation, as plain text.
@@ -108,12 +119,16 @@ export type VisualServerFrame = {
108
119
  } | {
109
120
  readonly kind: 'model';
110
121
  readonly model: VisualRenderedModel;
122
+ /**
123
+ * The saved views again, because a recompile moves what their queries
124
+ * match and the tree states those counts. Required rather than optional:
125
+ * a recompile that shipped a graph without them would leave every count
126
+ * beside it describing the model before the commit.
127
+ */
128
+ readonly views: readonly VisualViewSummary[];
111
129
  } | {
112
130
  readonly kind: 'filter-result';
113
131
  readonly result: VisualFilterResultPayload;
114
- } | {
115
- readonly kind: 'view-save-result';
116
- readonly result: VisualViewSaveResultPayload;
117
132
  } | {
118
133
  readonly kind: 'apply-result';
119
134
  readonly result: VisualApplyResultPayload;
@@ -0,0 +1,116 @@
1
+ import type { Diagnostic } from "../../compiler.js";
2
+ import type { PendingWrite, SourceStore } from "../../source-store.js";
3
+ import type { ResolvedWorkspace } from "../../workspace.js";
4
+ import type { VisualDiagnostic, VisualViewOperation } from "./protocol-contract.js";
5
+ import type { ProjectionDefinition, ProjectionExclusion, ProjectionQuery } from "../../projection.js";
6
+ import type { ResolvedProfileContext, SemanticGraph } from "../../compiler.js";
7
+ import type { VisualKindOption, VisualViewSummary } from "./protocol-contract.js";
8
+ import type { VisualRenderedModel } from "./wire.js";
9
+ /**
10
+ * What a workspace looks like to the editor, however the editor is being run
11
+ * (#252).
12
+ *
13
+ * These were the session server's, and the session server is one host now: an
14
+ * embedder mounting the editor over its own store computes the same model, the
15
+ * same vocabulary and the same view counts, and two definitions of any of them
16
+ * would be two answers to the same question. So the arithmetic lives here and
17
+ * the orchestration - which bytes to read, which digests to pin, what a layout
18
+ * sidecar says - stays with whoever owns those.
19
+ *
20
+ * Browser-safe: no `node:`, no filesystem, no session. Everything is a pure
21
+ * function of a compile result.
22
+ */
23
+ /**
24
+ * The kinds a browser may offer, each with the core kind it descends from.
25
+ *
26
+ * `[0]` is the nearest declared ancestor, the same reading
27
+ * `projectGraphForCanvas` gives an authored subject its `coreKindLabel`. A kind
28
+ * with no lineage IS a core kind and stands for itself. Without the core label
29
+ * an editor can read a palette but cannot judge it: the ArchiMate table is
30
+ * keyed on core kinds, so offering an extension kind unchecked puts a `YM404`
31
+ * one click away.
32
+ */
33
+ export declare const kindOptionsOf: (lineages: ReadonlyMap<string, readonly string[]>) => readonly VisualKindOption[];
34
+ /**
35
+ * How many SUBJECTS a query matches, which is not the size of its match set.
36
+ *
37
+ * A `SemanticGraph`'s subjects are concepts and relationships together, so the
38
+ * match set returns both — right for narrowing a canvas that draws edges as
39
+ * well as nodes, and wrong for a number sitting beside a view called its
40
+ * subject count. A view over three components with two relationships between
41
+ * them would read as five, and the reviewer counting boxes would find three.
42
+ */
43
+ export declare const conceptCountOf: (graph: SemanticGraph, query: ProjectionQuery, profileContext: ResolvedProfileContext) => number;
44
+ /**
45
+ * Rebuilds the shared editor workspace from one successful compile.
46
+ *
47
+ * The caller owns metadata which cannot be inferred from a graph (authority,
48
+ * source revisions, layouts, and initial view); this helper owns all derived
49
+ * canvas, vocabulary, and view-count arithmetic.
50
+ */
51
+ export declare const renderedWorkspaceOf: (compiled: {
52
+ readonly graph: SemanticGraph;
53
+ readonly profileContext: ResolvedProfileContext;
54
+ }, views: readonly VisualViewSummary[], metadata: Omit<VisualRenderedModel, "graph" | "vocabulary">) => {
55
+ readonly model: VisualRenderedModel;
56
+ readonly views: readonly VisualViewSummary[];
57
+ };
58
+ /** Every subject a query draws, concepts and relationships alike. */
59
+ export declare const matchedIdsOf: (graph: SemanticGraph, query: ProjectionQuery, profileContext: ResolvedProfileContext) => readonly string[];
60
+ /** Every concept a query dropped, and the facet that dropped it (#248). */
61
+ export declare const exclusionsOf: (graph: SemanticGraph, query: ProjectionQuery, profileContext: ResolvedProfileContext) => readonly ProjectionExclusion[];
62
+ /**
63
+ * One saved view, as the rail reads it. `subjectCount` is the caller's,
64
+ * because counting needs a compiled graph and a session builds its first list
65
+ * before it has one.
66
+ */
67
+ export declare const viewSummaryOf: (projection: ProjectionDefinition, path: string, subjectCount?: number) => VisualViewSummary;
68
+ /**
69
+ * Applies a landed view batch to a host-owned list.
70
+ *
71
+ * Writes are read back through the callback, so a malformed or otherwise
72
+ * unreadable landed document cannot become a fabricated summary.
73
+ */
74
+ export declare const adoptLandedViews: (views: readonly VisualViewSummary[], operations: readonly VisualViewOperation[], readSummary: (path: string) => VisualViewSummary | undefined) => readonly VisualViewSummary[];
75
+ /**
76
+ * The document a diagnostic the runtime minted itself points at. Not a file:
77
+ * these are about the session rather than about anything the workspace holds.
78
+ */
79
+ export declare const VISUAL_SERVER_DOCUMENT = "visual-session-server";
80
+ export declare const published: (diagnostics: readonly Diagnostic[], sources: readonly {
81
+ readonly path: string;
82
+ readonly source: string;
83
+ }[]) => readonly VisualDiagnostic[];
84
+ export declare const serverDiagnostic: (code: string, message: string, pointer?: string) => VisualDiagnostic;
85
+ /**
86
+ * Whether the workspace would load a projection written at this path.
87
+ *
88
+ * This is a DIRECTORY check, not a pattern match, and deliberately so: ADR
89
+ * 0100 recorded that the manifest's pattern dialect has never been named - it
90
+ * is whatever `globSync` happens to support, undocumented and untested past
91
+ * one wildcard - so a matcher written here would be inventing the dialect
92
+ * rather than honouring it. A directory that already holds a projection the
93
+ * manifest resolved is a directory the manifest demonstrably reaches.
94
+ *
95
+ * A workspace with no projections at all has nothing to demonstrate, so the
96
+ * default directory is allowed: refusing there would make the first view in a
97
+ * fresh workspace impossible to create.
98
+ */
99
+ export declare const projectionDirectoryIsCovered: (path: string, workspace: ResolvedWorkspace) => boolean;
100
+ /**
101
+ * Turns staged view operations into pending writes, refusing before anything
102
+ * is written (ADR 0103).
103
+ *
104
+ * A `write-view` is validated through the same `loadProjection` the CLI's own
105
+ * projection writes go through, so a document the schema would reject never
106
+ * reaches the store. A `delete-view` names a revision, which is what makes a
107
+ * removal refusable rather than a silent success on a file someone else
108
+ * already changed.
109
+ */
110
+ export declare const planViewWrites: (operations: readonly VisualViewOperation[], workspace: ResolvedWorkspace, store: SourceStore) => {
111
+ readonly ok: true;
112
+ readonly writes: readonly PendingWrite[];
113
+ } | {
114
+ readonly ok: false;
115
+ readonly diagnostics: readonly VisualDiagnostic[];
116
+ };