@jarenjs/charts 0.34.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 (56) hide show
  1. package/README.md +293 -0
  2. package/dist/types/component/index.d.ts +95 -0
  3. package/dist/types/core/axis.d.ts +77 -0
  4. package/dist/types/core/cartesian.d.ts +127 -0
  5. package/dist/types/core/chart.d.ts +58 -0
  6. package/dist/types/core/domain.d.ts +96 -0
  7. package/dist/types/core/marks.d.ts +86 -0
  8. package/dist/types/core/palette.d.ts +72 -0
  9. package/dist/types/core/scale.d.ts +59 -0
  10. package/dist/types/core/session.d.ts +85 -0
  11. package/dist/types/core/stream-adapter.d.ts +187 -0
  12. package/dist/types/index.d.ts +35 -0
  13. package/dist/types/transforms/benchmark-adapter.d.ts +169 -0
  14. package/dist/types/transforms/mermaid-adapter.d.ts +30 -0
  15. package/dist/types/types/bar.d.ts +213 -0
  16. package/dist/types/types/boxplot.d.ts +116 -0
  17. package/dist/types/types/candlestick.d.ts +218 -0
  18. package/dist/types/types/gauge.d.ts +68 -0
  19. package/dist/types/types/heatmap.d.ts +104 -0
  20. package/dist/types/types/line.d.ts +272 -0
  21. package/dist/types/types/map.d.ts +137 -0
  22. package/dist/types/types/pie.d.ts +146 -0
  23. package/dist/types/types/radar.d.ts +89 -0
  24. package/dist/types/types/sankey.d.ts +100 -0
  25. package/dist/types/types/scatter.d.ts +80 -0
  26. package/dist/types/types/streamgraph.d.ts +75 -0
  27. package/dist/types/types/treemap.d.ts +118 -0
  28. package/package.json +76 -0
  29. package/schemas/chart-definition.schema.json +448 -0
  30. package/src/component/index.js +125 -0
  31. package/src/core/axis.js +221 -0
  32. package/src/core/cartesian.js +192 -0
  33. package/src/core/chart.js +101 -0
  34. package/src/core/domain.js +123 -0
  35. package/src/core/marks.js +110 -0
  36. package/src/core/palette.js +126 -0
  37. package/src/core/scale.js +106 -0
  38. package/src/core/session.js +0 -0
  39. package/src/core/stream-adapter.js +613 -0
  40. package/src/index.js +40 -0
  41. package/src/transforms/benchmark-adapter.js +298 -0
  42. package/src/transforms/mermaid-adapter.js +19 -0
  43. package/src/types/bar.js +276 -0
  44. package/src/types/boxplot.js +216 -0
  45. package/src/types/candlestick.js +274 -0
  46. package/src/types/gauge.js +140 -0
  47. package/src/types/heatmap.js +176 -0
  48. package/src/types/line.js +349 -0
  49. package/src/types/map.js +378 -0
  50. package/src/types/pie.js +163 -0
  51. package/src/types/radar.js +224 -0
  52. package/src/types/sankey.js +391 -0
  53. package/src/types/scatter.js +148 -0
  54. package/src/types/streamgraph.js +158 -0
  55. package/src/types/treemap.js +322 -0
  56. package/styles/charts.css +83 -0
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @file The treemap chart type: part-of-whole tiles by area, laid out
3
+ * with the squarified algorithm (rows chosen to keep tile aspect ratios
4
+ * near 1). Data shape:
5
+ *
6
+ * data = { items: [{ label, value }] } // flat
7
+ * data = { items: [{ label, children: [{label, value}] }] } // grouped
8
+ * config = { type:'treemap', title?, aspect? }
9
+ *
10
+ * The AST is a flat list of unit-square tiles (`x0..x1`/`y0..y1` in
11
+ * [0,1], `y` growing downward — a treemap has no axes, so reading order
12
+ * wins). Squarification optimizes *rendered* aspect ratios, so the
13
+ * build needs the drawing's width:height ratio — that is `aspect`
14
+ * (default 1.6), carried in the AST so the render maps height from
15
+ * width with the same value.
16
+ *
17
+ * One level of grouping is supported: an item with `children` is a
18
+ * group whose value is its children's sum. Groups squarify against each
19
+ * other, then each group's children squarify inside its rect below a
20
+ * header band that names it — the package→module reading. Groups take a
21
+ * palette color each, so a group is one hue and its children are told
22
+ * apart by the hairline between them. A childless item in a grouped
23
+ * chart becomes a group of one, so no data is dropped either way; a
24
+ * chart with no `children` anywhere lays out exactly as it always has.
25
+ */
26
+ export type TreemapTileAST = {
27
+ label: string;
28
+ value: number;
29
+ /**
30
+ * fraction of the total (0..1)
31
+ */
32
+ frac: number;
33
+ x0: number;
34
+ y0: number;
35
+ x1: number;
36
+ y1: number;
37
+ /**
38
+ * the group this tile belongs to (null when flat)
39
+ */
40
+ group: string | null;
41
+ /**
42
+ * palette index for the fill
43
+ */
44
+ swatch: number;
45
+ };
46
+ export type TreemapGroupAST = {
47
+ label: string;
48
+ /**
49
+ * the children's sum
50
+ */
51
+ value: number;
52
+ /**
53
+ * fraction of the total (0..1)
54
+ */
55
+ frac: number;
56
+ x0: number;
57
+ y0: number;
58
+ x1: number;
59
+ y1: number;
60
+ /**
61
+ * height of the naming band at the group's top
62
+ * (a fraction of the map height; the children fill what is left)
63
+ */
64
+ header: number;
65
+ /**
66
+ * palette index
67
+ */
68
+ swatch: number;
69
+ };
70
+ export type TreemapAST = {
71
+ type: 'treemap';
72
+ title: string | null;
73
+ /**
74
+ * layout width:height ratio
75
+ */
76
+ aspect: number;
77
+ total: number;
78
+ /**
79
+ * value-descending, grouped when
80
+ * `groups` is non-empty (descending inside each group)
81
+ */
82
+ tiles: TreemapTileAST[];
83
+ /**
84
+ * empty for a flat treemap
85
+ */
86
+ groups: TreemapGroupAST[];
87
+ };
88
+ /**
89
+ * Build the geometry-free treemap AST.
90
+ * @param {any} data
91
+ * @param {any} [config]
92
+ * @returns {TreemapAST}
93
+ */
94
+ export declare function buildTreemapAST(data: any, config?: any): TreemapAST;
95
+ /**
96
+ * Render a treemap AST to a pure-vnode SVG: inset tile rects on the
97
+ * categorical palette, labels inside the tiles that fit them (ink
98
+ * picked by fill luminance), and a label+share `<title>` on every tile.
99
+ * A grouped treemap adds the group's name in its header band and a
100
+ * hairline between same-hue siblings — both only when there are groups,
101
+ * so a flat treemap renders exactly as it did before grouping existed.
102
+ * @param {TreemapAST} ast
103
+ * @param {{tokens: Record<string,string>, cssVars: Record<string,string>}} theme
104
+ * @param {string} hash
105
+ * @param {{rootClass?: string, keyPrefix?: string, palette?: readonly string[], width?: number,
106
+ * tooltip?: import('../core/marks.js').ChartTooltipSpec}} [options]
107
+ * @returns {any}
108
+ */
109
+ export declare function renderTreemapAST(ast: TreemapAST, theme: {
110
+ tokens: Record<string, string>;
111
+ cssVars: Record<string, string>;
112
+ }, hash: string, options?: {
113
+ rootClass?: string;
114
+ keyPrefix?: string;
115
+ palette?: readonly string[];
116
+ width?: number;
117
+ tooltip?: import('../core/marks.js').ChartTooltipSpec;
118
+ }): any;
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@jarenjs/charts",
3
+ "private": false,
4
+ "version": "0.34.0",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "types": "./dist/types/index.d.ts",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/types/index.d.ts",
12
+ "default": "./src/index.js"
13
+ },
14
+ "./component": {
15
+ "types": "./dist/types/component/index.d.ts",
16
+ "default": "./src/component/index.js"
17
+ },
18
+ "./stream-adapter": {
19
+ "types": "./dist/types/core/stream-adapter.d.ts",
20
+ "default": "./src/core/stream-adapter.js"
21
+ },
22
+ "./transforms/mermaid-adapter": {
23
+ "types": "./dist/types/transforms/mermaid-adapter.d.ts",
24
+ "default": "./src/transforms/mermaid-adapter.js"
25
+ },
26
+ "./transforms/benchmark-adapter": {
27
+ "types": "./dist/types/transforms/benchmark-adapter.d.ts",
28
+ "default": "./src/transforms/benchmark-adapter.js"
29
+ },
30
+ "./styles/charts.css": "./styles/charts.css",
31
+ "./schemas/*": "./schemas/*",
32
+ "./package.json": "./package.json"
33
+ },
34
+ "files": [
35
+ "dist/types/",
36
+ "src/",
37
+ "schemas/",
38
+ "styles/"
39
+ ],
40
+ "description": "Headless charts for the jaren suite: chart definitions compiled to a geometry-free AST and rendered as pure-vnode SVG through @jarenjs/view — SSR-able, structurally shared, themeable via host-linked tokens",
41
+ "author": "joham",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/jklarenbeek/jarenjs.git",
45
+ "directory": "components/charts"
46
+ },
47
+ "license": "MIT",
48
+ "engines": {
49
+ "node": ">=24"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public",
53
+ "registry": "https://registry.npmjs.org/"
54
+ },
55
+ "keywords": [
56
+ "jaren",
57
+ "charts",
58
+ "svg",
59
+ "pie",
60
+ "bar",
61
+ "line",
62
+ "scatter",
63
+ "headless",
64
+ "streaming",
65
+ "vnode"
66
+ ],
67
+ "scripts": {
68
+ "build": "npm run build:types",
69
+ "build:types": "tsc -p tsconfig.json",
70
+ "prepack": "npm run build:types"
71
+ },
72
+ "dependencies": {
73
+ "@jarenjs/core": "^0.34.0",
74
+ "@jarenjs/view": "^0.34.0"
75
+ }
76
+ }
@@ -0,0 +1,448 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://jarenjs.github.io/schemas/chart-definition.schema.json",
4
+ "title": "Chart definition",
5
+ "description": "A self-contained chart definition for @jarenjs/charts compileChart: the type, presentation fields, and (inline) the data fields the type reads. Streaming callers may omit the data fields and pass data separately.",
6
+ "type": "object",
7
+ "required": ["type"],
8
+ "properties": {
9
+ "type": {
10
+ "enum": ["pie", "bar", "line", "scatter", "candlestick", "radar", "gauge", "boxplot", "heatmap", "treemap", "streamgraph", "sankey", "map"]
11
+ },
12
+ "title": {
13
+ "type": ["string", "null"]
14
+ },
15
+ "stream": { "$ref": "#/$defs/streamSpec" }
16
+ },
17
+ "allOf": [
18
+ {
19
+ "if": { "properties": { "type": { "const": "pie" } } },
20
+ "then": {
21
+ "properties": {
22
+ "donut": {
23
+ "description": "true for the default hole fraction, or the hole radius as a fraction of the outer radius",
24
+ "type": ["boolean", "number"],
25
+ "exclusiveMinimum": 0,
26
+ "exclusiveMaximum": 1
27
+ },
28
+ "slices": {
29
+ "type": "array",
30
+ "items": { "$ref": "#/$defs/slice" }
31
+ }
32
+ }
33
+ }
34
+ },
35
+ {
36
+ "if": { "properties": { "type": { "const": "bar" } } },
37
+ "then": {
38
+ "properties": {
39
+ "stacked": { "type": "boolean" },
40
+ "log": { "type": "boolean" },
41
+ "orient": { "enum": ["v", "h"] },
42
+ "catLabel": { "type": ["string", "null"] },
43
+ "valLabel": { "type": ["string", "null"] },
44
+ "categories": {
45
+ "type": "array",
46
+ "items": { "type": "string" }
47
+ },
48
+ "series": {
49
+ "type": "array",
50
+ "items": { "$ref": "#/$defs/barSeries" }
51
+ }
52
+ }
53
+ }
54
+ },
55
+ {
56
+ "if": { "properties": { "type": { "const": "line" } } },
57
+ "then": {
58
+ "properties": {
59
+ "x": { "enum": ["linear", "time"] },
60
+ "log": { "type": "boolean" },
61
+ "markers": { "type": "boolean" },
62
+ "xLabel": { "type": ["string", "null"] },
63
+ "yLabel": { "type": ["string", "null"] },
64
+ "domain": { "$ref": "#/$defs/domainPolicy" },
65
+ "series": {
66
+ "type": "array",
67
+ "items": { "$ref": "#/$defs/lineSeries" }
68
+ }
69
+ }
70
+ }
71
+ },
72
+ {
73
+ "if": { "properties": { "type": { "const": "candlestick" } } },
74
+ "then": {
75
+ "properties": {
76
+ "xLabel": { "type": ["string", "null"] },
77
+ "yLabel": { "type": ["string", "null"] },
78
+ "domain": { "$ref": "#/$defs/domainPolicy" },
79
+ "candles": {
80
+ "type": "array",
81
+ "items": { "$ref": "#/$defs/candle" }
82
+ }
83
+ }
84
+ }
85
+ },
86
+ {
87
+ "if": { "properties": { "type": { "const": "scatter" } } },
88
+ "then": {
89
+ "properties": {
90
+ "xLog": { "type": "boolean" },
91
+ "yLog": { "type": "boolean" },
92
+ "refY": { "type": "number" },
93
+ "refLabel": { "type": ["string", "null"] },
94
+ "xLabel": { "type": ["string", "null"] },
95
+ "yLabel": { "type": ["string", "null"] },
96
+ "points": {
97
+ "type": "array",
98
+ "items": { "$ref": "#/$defs/point" }
99
+ }
100
+ }
101
+ }
102
+ },
103
+ {
104
+ "if": { "properties": { "type": { "const": "radar" } } },
105
+ "then": {
106
+ "properties": {
107
+ "max": { "type": "number", "exclusiveMinimum": 0 },
108
+ "axes": {
109
+ "type": "array",
110
+ "items": { "type": "string" }
111
+ },
112
+ "series": {
113
+ "type": "array",
114
+ "items": { "$ref": "#/$defs/valueSeries" }
115
+ }
116
+ }
117
+ }
118
+ },
119
+ {
120
+ "if": { "properties": { "type": { "const": "gauge" } } },
121
+ "then": {
122
+ "properties": {
123
+ "value": { "type": "number" },
124
+ "min": { "type": "number" },
125
+ "max": { "type": "number" },
126
+ "unit": { "type": ["string", "null"] },
127
+ "tone": { "$ref": "#/$defs/tone" }
128
+ }
129
+ }
130
+ },
131
+ {
132
+ "if": { "properties": { "type": { "const": "boxplot" } } },
133
+ "then": {
134
+ "properties": {
135
+ "catLabel": { "type": ["string", "null"] },
136
+ "valLabel": { "type": ["string", "null"] },
137
+ "boxes": {
138
+ "type": "array",
139
+ "items": { "$ref": "#/$defs/box" }
140
+ }
141
+ }
142
+ }
143
+ },
144
+ {
145
+ "if": { "properties": { "type": { "const": "heatmap" } } },
146
+ "then": {
147
+ "properties": {
148
+ "log": { "type": "boolean" },
149
+ "xLabel": { "type": ["string", "null"] },
150
+ "yLabel": { "type": ["string", "null"] },
151
+ "xLabels": {
152
+ "type": "array",
153
+ "items": { "type": "string" }
154
+ },
155
+ "yLabels": {
156
+ "type": "array",
157
+ "items": { "type": "string" }
158
+ },
159
+ "values": {
160
+ "description": "values[yi][xi], row-major from the top row; null marks a missing cell",
161
+ "type": "array",
162
+ "items": {
163
+ "type": "array",
164
+ "items": { "type": ["number", "null"] }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ },
170
+ {
171
+ "if": { "properties": { "type": { "const": "treemap" } } },
172
+ "then": {
173
+ "properties": {
174
+ "aspect": { "type": "number", "exclusiveMinimum": 0 },
175
+ "items": {
176
+ "description": "leaves, or one level of groups; a chart with any group treats a bare leaf as a group of one",
177
+ "type": "array",
178
+ "items": {
179
+ "anyOf": [
180
+ { "$ref": "#/$defs/treemapItem" },
181
+ { "$ref": "#/$defs/treemapGroup" }
182
+ ]
183
+ }
184
+ }
185
+ }
186
+ }
187
+ },
188
+ {
189
+ "if": { "properties": { "type": { "const": "streamgraph" } } },
190
+ "then": {
191
+ "properties": {
192
+ "xLabel": { "type": ["string", "null"] },
193
+ "xs": {
194
+ "type": "array",
195
+ "items": { "type": "number" }
196
+ },
197
+ "series": {
198
+ "type": "array",
199
+ "items": { "$ref": "#/$defs/valueSeries" }
200
+ }
201
+ }
202
+ }
203
+ },
204
+ {
205
+ "if": { "properties": { "type": { "const": "sankey" } } },
206
+ "then": {
207
+ "properties": {
208
+ "nodes": {
209
+ "type": "array",
210
+ "items": {
211
+ "anyOf": [
212
+ { "type": "string" },
213
+ {
214
+ "type": "object",
215
+ "required": ["name"],
216
+ "properties": { "name": { "type": "string" } }
217
+ }
218
+ ]
219
+ }
220
+ },
221
+ "links": {
222
+ "type": "array",
223
+ "items": { "$ref": "#/$defs/sankeyLink" }
224
+ }
225
+ }
226
+ }
227
+ },
228
+ {
229
+ "if": { "properties": { "type": { "const": "map" } } },
230
+ "then": {
231
+ "properties": {
232
+ "value": {
233
+ "description": "feature property to shade by; absent leaves the map unshaded",
234
+ "type": "string"
235
+ },
236
+ "label": {
237
+ "description": "feature property to name features by (default 'name')",
238
+ "type": "string"
239
+ },
240
+ "log": { "type": "boolean" },
241
+ "aspect": { "type": "number", "exclusiveMinimum": 0 },
242
+ "simplify": {
243
+ "description": "Douglas-Peucker tolerance as a fraction of the frame's width, or false to keep every vertex",
244
+ "type": ["number", "boolean"],
245
+ "minimum": 0
246
+ },
247
+ "features": {
248
+ "description": "GeoJSON: a FeatureCollection, or an array of Features or geometries",
249
+ "type": ["object", "array"]
250
+ },
251
+ "points": {
252
+ "type": "array",
253
+ "items": { "$ref": "#/$defs/mapPoint" }
254
+ }
255
+ }
256
+ }
257
+ }
258
+ ],
259
+ "$defs": {
260
+ "streamSpec": {
261
+ "description": "createStreamAdapter mapping: how reader events accumulate into this chart's data",
262
+ "type": "object",
263
+ "properties": {
264
+ "recordPath": {
265
+ "type": "array",
266
+ "items": { "type": ["string", "number"] }
267
+ },
268
+ "recordBoundary": { "enum": ["path", "document"] },
269
+ "xField": { "type": "string" },
270
+ "yField": { "type": "string" },
271
+ "seriesField": { "type": "string" },
272
+ "maxPoints": { "type": "integer", "minimum": 1 }
273
+ }
274
+ },
275
+ "slice": {
276
+ "type": "object",
277
+ "required": ["label", "value"],
278
+ "properties": {
279
+ "label": { "type": "string" },
280
+ "value": { "type": "number", "minimum": 0 }
281
+ }
282
+ },
283
+ "tone": {
284
+ "enum": ["win", "loss", null]
285
+ },
286
+ "barSeries": {
287
+ "type": "object",
288
+ "required": ["name", "values"],
289
+ "properties": {
290
+ "name": { "type": "string" },
291
+ "values": {
292
+ "type": "array",
293
+ "items": { "type": ["number", "null"] }
294
+ },
295
+ "tone": { "$ref": "#/$defs/tone" },
296
+ "tones": {
297
+ "type": "array",
298
+ "items": { "$ref": "#/$defs/tone" }
299
+ }
300
+ }
301
+ },
302
+ "lineSeries": {
303
+ "type": "object",
304
+ "required": ["name", "points"],
305
+ "properties": {
306
+ "name": { "type": "string" },
307
+ "points": {
308
+ "type": "array",
309
+ "items": { "$ref": "#/$defs/point" }
310
+ }
311
+ }
312
+ },
313
+ "point": {
314
+ "type": "object",
315
+ "required": ["x", "y"],
316
+ "properties": {
317
+ "x": { "type": "number" },
318
+ "y": { "type": "number" },
319
+ "tone": { "$ref": "#/$defs/tone" }
320
+ }
321
+ },
322
+ "candle": {
323
+ "type": "object",
324
+ "required": ["t", "open", "high", "low", "close"],
325
+ "properties": {
326
+ "t": { "type": "number" },
327
+ "open": { "type": "number" },
328
+ "high": { "type": "number" },
329
+ "low": { "type": "number" },
330
+ "close": { "type": "number" }
331
+ }
332
+ },
333
+ "valueSeries": {
334
+ "description": "a named series of index-aligned values (radar axes / streamgraph samples)",
335
+ "type": "object",
336
+ "required": ["name", "values"],
337
+ "properties": {
338
+ "name": { "type": "string" },
339
+ "values": {
340
+ "type": "array",
341
+ "items": { "type": ["number", "null"] }
342
+ }
343
+ }
344
+ },
345
+ "box": {
346
+ "description": "one boxplot category: raw samples in `values`, or a precomputed five-number summary",
347
+ "type": "object",
348
+ "required": ["label"],
349
+ "properties": {
350
+ "label": { "type": "string" },
351
+ "values": {
352
+ "type": "array",
353
+ "items": { "type": ["number", "null"] }
354
+ },
355
+ "min": { "type": "number" },
356
+ "q1": { "type": "number" },
357
+ "med": { "type": "number" },
358
+ "q3": { "type": "number" },
359
+ "max": { "type": "number" },
360
+ "outliers": {
361
+ "type": "array",
362
+ "items": { "type": "number" }
363
+ }
364
+ },
365
+ "anyOf": [
366
+ { "required": ["values"] },
367
+ { "required": ["min", "q1", "med", "q3", "max"] }
368
+ ]
369
+ },
370
+ "treemapItem": {
371
+ "type": "object",
372
+ "required": ["label", "value"],
373
+ "properties": {
374
+ "label": { "type": "string" },
375
+ "value": { "type": "number", "exclusiveMinimum": 0 }
376
+ }
377
+ },
378
+ "treemapGroup": {
379
+ "description": "one hierarchy level: the group's value is its children's sum",
380
+ "type": "object",
381
+ "required": ["label", "children"],
382
+ "properties": {
383
+ "label": { "type": "string" },
384
+ "children": {
385
+ "type": "array",
386
+ "minItems": 1,
387
+ "items": { "$ref": "#/$defs/treemapItem" }
388
+ }
389
+ }
390
+ },
391
+ "sankeyLink": {
392
+ "description": "a flow from source to target; endpoints are node names or node indexes",
393
+ "type": "object",
394
+ "required": ["source", "target", "value"],
395
+ "properties": {
396
+ "source": { "type": ["string", "integer"] },
397
+ "target": { "type": ["string", "integer"] },
398
+ "value": { "type": "number", "exclusiveMinimum": 0 }
399
+ }
400
+ },
401
+ "mapPoint": {
402
+ "description": "a place marker: the convenience path for a caller holding positions rather than GeoJSON",
403
+ "type": "object",
404
+ "required": ["at"],
405
+ "properties": {
406
+ "at": {
407
+ "description": "a GeoJSON position: [longitude, latitude]",
408
+ "type": "array",
409
+ "minItems": 2,
410
+ "maxItems": 3,
411
+ "items": { "type": "number" },
412
+ "prefixItems": [
413
+ { "type": "number", "minimum": -180, "maximum": 180 },
414
+ { "type": "number", "minimum": -90, "maximum": 90 }
415
+ ]
416
+ },
417
+ "label": { "type": "string" },
418
+ "value": { "type": "number" }
419
+ }
420
+ },
421
+ "domainPolicy": {
422
+ "description": "domain-stability policy: a quantized sliding x window and/or pinned or step-quantized y bounds, so most streaming ticks keep the scales still",
423
+ "type": "object",
424
+ "properties": {
425
+ "x": {
426
+ "type": "object",
427
+ "required": ["window"],
428
+ "properties": {
429
+ "window": { "type": "number", "exclusiveMinimum": 0 },
430
+ "slide": { "type": "number", "exclusiveMinimum": 0 }
431
+ }
432
+ },
433
+ "y": {
434
+ "anyOf": [
435
+ { "const": "step" },
436
+ {
437
+ "type": "object",
438
+ "properties": {
439
+ "min": { "type": "number" },
440
+ "max": { "type": "number" }
441
+ }
442
+ }
443
+ ]
444
+ }
445
+ }
446
+ }
447
+ }
448
+ }