@miragon/modeler-api 1.0.0 → 1.2.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.
@@ -1,4 +1,5 @@
1
1
  import { CollaborativeModeler } from './index.js';
2
+ export { apiVersionsCompatible } from './index.js';
2
3
 
3
4
  /**
4
5
  * The conformance test kit — every law of the CollaborativeModeler contract
@@ -13,7 +14,9 @@ import { CollaborativeModeler } from './index.js';
13
14
  * assertion-library dependency. A case that CANNOT (fully) run because the
14
15
  * harness left an optional field undefined resolves with `{ skipped }` and
15
16
  * registerConformance reports it (console.warn by default) — a law the kit
16
- * did not verify is never a silent green.
17
+ * did not verify is never a silent green. A RECOMMENDATION the modeler does
18
+ * not follow resolves with `{ advisories }`, reported the same way and never
19
+ * a failure: a recommendation becomes a law only in a major release.
17
20
  */
18
21
 
19
22
  interface UserMutation {
@@ -27,7 +30,8 @@ interface ConformanceHarness {
27
30
  * document with a non-zero size (`attachedHost()` below) — diagram-js
28
31
  * engines compute viewboxes from the host's client size, so a detached
29
32
  * or 0×0 host yields NaN view state; they also need a real browser
30
- * (vitest browser mode / Playwright), jsdom has no getBBox
33
+ * (vitest browser mode / Playwright), jsdom has no getBBox. Keep
34
+ * `fitOnImport` at its default: the L5 cases check the modeler's own fit
31
35
  */
32
36
  create(): Promise<CollaborativeModeler>;
33
37
  /** a fresh READ-ONLY instance (`meta.editable === false`) — the viewer
@@ -51,10 +55,23 @@ interface ConformanceHarness {
51
55
  * NON-importable inputs (half-typed peer text, wrong notation) — the kit
52
56
  * verifies importText REJECTS, leaves no trace and stays usable (L7).
53
57
  * `undefined` = not provided (the L7 cases report a SKIP); `[]` = the
54
- * deliberate statement that this notation has no non-importable text
55
- * (the lenient line DSLs keep every line, so nothing rejects)
58
+ * deliberate statement that this notation has no LISTED non-importable
59
+ * text (the lenient line DSLs keep every line, so nothing rejects). The
60
+ * probes below run either way, so a parser that does reject some input
61
+ * is held to L7 on it
56
62
  */
57
63
  invalidTexts?: string[];
64
+ /**
65
+ * extra inputs for the L7 probe case, on top of the probes the kit derives
66
+ * from every canonical text: its first half, its first third, the text
67
+ * without its last character and the text followed by a NUL character.
68
+ * Unlike `invalidTexts`, a probe need not reject: one that imports must
69
+ * be silent (L1) with `exportText` total (L6), one that rejects must leave
70
+ * no trace (L7). Add the damaged forms your parser is likely to meet (a
71
+ * cut-off JSON object, an unclosed block). `undefined` and `[]` both mean
72
+ * "the derived probes only" — the probe case never skips
73
+ */
74
+ probeTexts?: string[];
58
75
  /**
59
76
  * importable documents carrying content the modeler does NOT understand
60
77
  * (vendor extensions, unknown lines, unknown JSON keys), each with the
@@ -80,8 +97,19 @@ interface ConformanceHarness {
80
97
  /**
81
98
  * perform ONE user-visible atomic ELEMENT command through the modeler's
82
99
  * own commands: ADD an element — never rename or delete one (the elements
83
- * case treats it as an edit unrelated to the existing elements, and L4
84
- * builds undo history from it, so it must be undoable)
100
+ * cases treat it as an edit unrelated to the existing elements and compare
101
+ * the ids two instances allocate for it, and L4 builds undo history from
102
+ * it, so it must be undoable). Let each call's element differ in content
103
+ * from the previous one (a counter the harness shares across instances,
104
+ * as the reference harness does): the collision case compares the ids two
105
+ * fresh instances allocate for one new element each, and an id derived
106
+ * from identical content (one that spells out the element's name)
107
+ * collides by design. The kit also runs it once on a
108
+ * fresh instance BEFORE the first import; a throw there is tolerated, and
109
+ * so is a call that never settles (a mutate waiting for the change event
110
+ * of a command the modeler ignores without a document): the kit stops
111
+ * waiting after 2 s — what a command does without a document is the
112
+ * modeler's choice
85
113
  */
86
114
  mutate(modeler: CollaborativeModeler): Promise<void> | void;
87
115
  /**
@@ -99,12 +127,65 @@ interface ConformanceHarness {
99
127
  /** trigger the modeler's redo; `undefined` with `undo` defined = the redo
100
128
  * half of the undo/redo case is reported as skipped */
101
129
  redo?(modeler: CollaborativeModeler): Promise<void> | void;
130
+ /**
131
+ * move ONE element read from `canonicalTexts[0]` (imported, then one
132
+ * `mutate` added an element) through the modeler's own commands, the way
133
+ * a user drags it, far enough that its box changes position after any
134
+ * snapping, rounding or clamping — and return that element's id
135
+ * (ElementInfo.id). The bounds case requires `elements.bounds` to report
136
+ * the moved box. `undefined` = that half of the bounds case is reported as
137
+ * skipped (only when the modeler has `bounds`)
138
+ */
139
+ moveElement?(modeler: CollaborativeModeler): Promise<string> | string;
140
+ /**
141
+ * open an INTERACTION on an element of `canonicalTexts[0]` (already
142
+ * imported) the way a user does — its label editor, or a popup with an
143
+ * input — and type input WITHOUT committing it (no Enter, no blur). The
144
+ * kit then imports over it (L1) and changes the view over it (L5), and
145
+ * neither may commit it. Type the same input on every call, one the
146
+ * serializer writes verbatim: the L5 case looks for it in the export and
147
+ * compares what a commit writes with and without a view change in
148
+ * between. `undefined` = the L1 and L5 interaction cases are reported as
149
+ * skipped
150
+ */
151
+ openInteraction?(modeler: CollaborativeModeler): Promise<void> | void;
152
+ /**
153
+ * the interaction open right now — the element it edits and its
154
+ * uncommitted input — or `undefined` when none is open. The kit reads it
155
+ * before and after a re-import of the same text to check the RECOMMENDED
156
+ * keep-and-reopen (an advisory, never a failure). `undefined` = that half
157
+ * of the L1 interaction case is reported as skipped
158
+ */
159
+ interactionState?(modeler: CollaborativeModeler): InteractionState | undefined;
160
+ /**
161
+ * commit the interaction `openInteraction` opened, the way a user does
162
+ * (Enter, or a click on the canvas). The L5 interaction case commits once
163
+ * without a view change, proving the typed input changes the document,
164
+ * and once after a host's view change, checking the RECOMMENDED keep-open
165
+ * (an advisory, never a failure). `undefined` = both halves are reported
166
+ * as skipped
167
+ */
168
+ commitInteraction?(modeler: CollaborativeModeler): Promise<void> | void;
169
+ }
170
+ /** an interaction in progress, as `ConformanceHarness.interactionState` reports it */
171
+ interface InteractionState {
172
+ /** the stable id of the element the interaction edits (ElementInfo.id) */
173
+ elementId: string;
174
+ /** the uncommitted input, when the interaction has one */
175
+ text?: string;
102
176
  }
103
177
  interface CaseOutcome {
104
178
  /** the case could not (fully) verify its law — the harness left the field
105
179
  * it needs undefined, or the modeler has no such optional surface; the
106
180
  * reason names which */
107
181
  skipped?: string;
182
+ /**
183
+ * RECOMMENDED behaviour the modeler does not show. The case passed — the
184
+ * contract only recommends what is missing (a candidate for a law in the
185
+ * next major) — and registerConformance reports each advice through
186
+ * `onAdvisory`, never as a failure
187
+ */
188
+ advisories?: string[];
108
189
  }
109
190
  interface ConformanceCase {
110
191
  name: string;
@@ -117,18 +198,17 @@ interface ConformanceCase {
117
198
  * are given to 100% × 100%, which then resolves against the wrapper
118
199
  */
119
200
  declare function attachedHost(width?: number, height?: number): HTMLElement;
120
- /**
121
- * apiVersion compatibility: same major — and, while the contract is 0.x,
122
- * the same minor too (pre-1.0 minors may break; bump-minor-pre-major)
123
- */
124
- declare function apiVersionsCompatible(a: string, b: string): boolean;
125
201
  declare function conformanceCases(): ConformanceCase[];
126
202
  interface RegisterOptions {
127
203
  /** how a SKIPPED case is reported — a law the kit could not (fully) verify
128
204
  * because the harness left its field undefined. Default: console.warn */
129
205
  onSkip?: (caseName: string, reason: string) => void;
206
+ /** how an ADVISORY is reported — a RECOMMENDED behaviour the modeler does
207
+ * not show; the case still passes. Called once per advice. Default:
208
+ * console.warn */
209
+ onAdvisory?: (caseName: string, advice: string) => void;
130
210
  }
131
211
  /** bind every case to a (name, fn) test runner — node:test, vitest, jest */
132
212
  declare function registerConformance(harness: ConformanceHarness, test: (name: string, fn: () => Promise<void>) => unknown, options?: RegisterOptions): void;
133
213
 
134
- export { type CaseOutcome, type ConformanceCase, type ConformanceHarness, type RegisterOptions, type UserMutation, apiVersionsCompatible, attachedHost, conformanceCases, registerConformance };
214
+ export { type CaseOutcome, type ConformanceCase, type ConformanceHarness, type InteractionState, type RegisterOptions, type UserMutation, attachedHost, conformanceCases, registerConformance };