bamtigraph 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.
@@ -0,0 +1,486 @@
1
+ /** Unix seconds, an explicit Date, or an ISO datetime with a timezone offset. */
2
+ type Timestamp = number | Date | string;
3
+ /** RGB or RGBA channels, hexadecimal notation, or a supported named color. */
4
+ type Color = string | readonly [number, number, number] | readonly [number, number, number, number];
5
+ type Values = ArrayLike<number | null | undefined>;
6
+ /** Normalized red, green, blue, and alpha bytes. */
7
+ type RGBA = [number, number, number, number];
8
+ type Interpolation = "linear" | "step-post";
9
+ /** Mutable preprocessing output, with NaN marking missing samples. */
10
+ interface Samples {
11
+ timestamps: number[];
12
+ values: number[];
13
+ }
14
+ interface LegendValues {
15
+ current: number | null;
16
+ average: number | null;
17
+ maximum: number | null;
18
+ }
19
+ interface SeriesOptions {
20
+ kind?: "line" | "area";
21
+ color?: Color;
22
+ outline?: Color | null;
23
+ lineWidth?: number;
24
+ baseline?: number;
25
+ interpolation?: Interpolation;
26
+ gapAfter?: number;
27
+ legendValues?: Partial<LegendValues> | null;
28
+ }
29
+ interface SeriesInput extends SeriesOptions {
30
+ name: string;
31
+ timestamps: ArrayLike<Timestamp>;
32
+ values: Values;
33
+ }
34
+ interface Series extends Readonly<Required<Omit<SeriesOptions, "legendValues" | "color" | "outline">>> {
35
+ readonly color: Readonly<RGBA>;
36
+ readonly outline: Readonly<RGBA> | null;
37
+ readonly name: string;
38
+ readonly timestamps: readonly number[];
39
+ readonly values: readonly number[];
40
+ readonly legendValues: Readonly<LegendValues> | null;
41
+ }
42
+ interface Tick {
43
+ time: Timestamp;
44
+ label: string;
45
+ }
46
+ interface TimeAxis {
47
+ start?: Timestamp | null;
48
+ end?: Timestamp | null;
49
+ mode?: "auto" | "daily" | "weekly" | "monthly" | "yearly" | "custom";
50
+ timezone?: string;
51
+ minorSeconds?: number | null;
52
+ majorSeconds?: number | null;
53
+ labelSeconds?: number | null;
54
+ labelFormat?: string | null;
55
+ labelOffsetSeconds?: number;
56
+ ticks?: readonly Tick[] | null;
57
+ minorTicks?: readonly Timestamp[] | null;
58
+ majorTicks?: readonly Timestamp[] | null;
59
+ }
60
+ interface YAxis {
61
+ minimum?: number | null;
62
+ maximum?: number | null;
63
+ majorStep?: number | null;
64
+ minorDivisions?: number;
65
+ base?: 1000 | 1024;
66
+ scaleFactor?: number | null;
67
+ suffix?: string | null;
68
+ decimals?: number | null;
69
+ legendDecimals?: number;
70
+ showZeroSuffix?: boolean;
71
+ }
72
+ type ColumnAnchors = readonly [
73
+ readonly [number, number],
74
+ readonly [number, number],
75
+ readonly [number, number]
76
+ ];
77
+ interface LegendLayout {
78
+ nameX?: number;
79
+ swatchX?: number;
80
+ swatchWidth?: number;
81
+ swatchHeight?: number;
82
+ referenceWidth?: number;
83
+ autoScaleColumns?: boolean;
84
+ compact?: ColumnAnchors;
85
+ expanded?: ColumnAnchors;
86
+ aligned?: ColumnAnchors;
87
+ }
88
+ interface Layout {
89
+ width?: number;
90
+ plotHeight?: number;
91
+ left?: number;
92
+ right?: number;
93
+ top?: number;
94
+ titleY?: number;
95
+ titleOffsetX?: number;
96
+ unitX?: number;
97
+ xLabelGap?: number;
98
+ yLabelGap?: number;
99
+ legendGap?: number;
100
+ legendRowHeight?: number;
101
+ legendBottom?: number;
102
+ legend?: "reference" | "aligned" | "none";
103
+ legendLayout?: LegendLayout;
104
+ antialias?: number;
105
+ pixelScale?: number;
106
+ }
107
+ interface Theme {
108
+ background?: Color;
109
+ canvas?: Color;
110
+ shadeLight?: Color;
111
+ shadeDark?: Color;
112
+ text?: Color;
113
+ minorGrid?: Color;
114
+ majorGrid?: Color;
115
+ axis?: Color;
116
+ arrow?: Color;
117
+ watermark?: Color;
118
+ frame?: Color;
119
+ gridFront?: boolean;
120
+ gridDash?: readonly [number, number];
121
+ titleSize?: number;
122
+ axisSize?: number;
123
+ unitSize?: number;
124
+ legendSize?: number;
125
+ watermarkSize?: number;
126
+ captionSize?: number;
127
+ titleAdvance?: number;
128
+ axisAdvance?: number;
129
+ legendAdvance?: number;
130
+ }
131
+ interface FontOptions {
132
+ mode?: "system" | "bitmap";
133
+ family?: string;
134
+ titleFamily?: string | null;
135
+ unitFamily?: string | null;
136
+ captionFamily?: string | null;
137
+ strictGlyphs?: boolean;
138
+ }
139
+ interface RuleStyle {
140
+ color?: Color;
141
+ width?: number;
142
+ dash?: readonly [number, number] | null;
143
+ }
144
+ interface HRule extends RuleStyle {
145
+ value: number;
146
+ }
147
+ interface VRule extends RuleStyle {
148
+ time: Timestamp;
149
+ }
150
+ /** Immutable chart input. Nested objects merge; arrays replace their defaults. */
151
+ interface ChartOptions {
152
+ series?: readonly SeriesInput[];
153
+ title?: string;
154
+ verticalLabel?: string;
155
+ watermark?: string;
156
+ timeAxis?: TimeAxis;
157
+ yAxis?: YAxis;
158
+ layout?: Layout;
159
+ theme?: Theme;
160
+ fonts?: FontOptions;
161
+ legendLabels?: readonly [string, string, string];
162
+ missingLabel?: string;
163
+ hRules?: readonly HRule[];
164
+ vRules?: readonly VRule[];
165
+ }
166
+ interface TrafficOptions extends ChartOptions {
167
+ gapAfter?: number;
168
+ }
169
+ interface Unit {
170
+ factor: number;
171
+ suffix: string;
172
+ }
173
+ interface Statistics {
174
+ name: string;
175
+ current: number | null;
176
+ average: number | null;
177
+ maximum: number | null;
178
+ minimum: number | null;
179
+ count: number;
180
+ missing: number;
181
+ displayOverride: LegendValues | null;
182
+ }
183
+ /** Serializable chart manifest. Missing statistics are null, not NaN. */
184
+ interface GraphMetadata {
185
+ version: string;
186
+ imageSize: [number, number];
187
+ logicalSize: [number, number];
188
+ plotBox: [number, number, number, number];
189
+ pixelScale: number;
190
+ title: string;
191
+ verticalLabel: string;
192
+ watermark: string;
193
+ timeRange: [number, number];
194
+ timezone: string;
195
+ timeMode: string;
196
+ yRange: [number, number];
197
+ yStep: number;
198
+ yUnit: Unit;
199
+ xLabels: {
200
+ time: number;
201
+ label: string;
202
+ x: number;
203
+ }[];
204
+ statistics: Statistics[];
205
+ statisticsPolicy: string;
206
+ font: {
207
+ mode: "system" | "bitmap";
208
+ family: string | null;
209
+ pixelAlphabet: string | null;
210
+ };
211
+ layout: Layout;
212
+ theme: Theme;
213
+ warnings: string[];
214
+ }
215
+ /** Row-major straight-alpha bytes. Pixel storage intentionally remains mutable. */
216
+ interface RGBAImage {
217
+ width: number;
218
+ height: number;
219
+ data: Uint8ClampedArray | Uint8Array;
220
+ }
221
+ interface PNGOptions {
222
+ metadata?: boolean;
223
+ }
224
+ interface NearestSample {
225
+ name: string;
226
+ index: number | null;
227
+ time: number | null;
228
+ value: number | null;
229
+ }
230
+ interface MountOptions {
231
+ interactive?: boolean;
232
+ ariaLabel?: string;
233
+ onHover?: (event: {
234
+ time: number;
235
+ samples: NearestSample[];
236
+ }) => void;
237
+ }
238
+ /** Mounted browser lifecycle. Destroy restores the canvas DOM position and attributes. */
239
+ interface Controller {
240
+ readonly canvas: HTMLCanvasElement;
241
+ readonly chart: Chart;
242
+ readonly result: RenderResult;
243
+ readonly destroyed: boolean;
244
+ update(patch: ChartOptions | Chart): RenderResult;
245
+ destroy(): void;
246
+ }
247
+ interface DashboardOptions {
248
+ gap?: number;
249
+ padding?: readonly [number, number, number, number];
250
+ background?: Color;
251
+ cropHeight?: number | null;
252
+ }
253
+ interface DashboardPanel {
254
+ chart: Chart;
255
+ caption?: string;
256
+ }
257
+ interface DashboardMetadata {
258
+ version: string;
259
+ imageSize: [number, number];
260
+ pixelScale: number;
261
+ panels: {
262
+ position: [number, number];
263
+ caption: string;
264
+ chart: GraphMetadata;
265
+ }[];
266
+ }
267
+ interface CounterOptions {
268
+ factor?: number;
269
+ onDecrease?: "gap" | "wrap";
270
+ counterBits?: number | null;
271
+ maxRate?: number | null;
272
+ }
273
+ interface AggregateOptions {
274
+ interval?: number;
275
+ method?: "mean" | "min" | "max" | "last" | "sum";
276
+ origin?: number;
277
+ minCoverage?: number;
278
+ expectedStep?: number | null;
279
+ maxBuckets?: number;
280
+ }
281
+ interface CSVColumn extends SeriesOptions {
282
+ column: string;
283
+ name?: string;
284
+ }
285
+ interface CSVOptions {
286
+ timestampColumn?: string;
287
+ columns?: readonly CSVColumn[];
288
+ maxRows?: number;
289
+ maxBytes?: number;
290
+ }
291
+ interface PixelDifference {
292
+ pixels: number;
293
+ exactPixels: number;
294
+ exactRatio: number;
295
+ toleranceRatio: number;
296
+ meanAbsoluteError: number;
297
+ rootMeanSquareError: number;
298
+ maxError: number;
299
+ differenceBox: [number, number, number, number] | null;
300
+ }
301
+ interface CompareOptions {
302
+ tolerance?: number;
303
+ box?: readonly [number, number, number, number] | null;
304
+ }
305
+ /** All chart fields after default merging and normalization. */
306
+ interface ResolvedChartOptions extends Omit<Required<ChartOptions>, "series" | "timeAxis" | "yAxis" | "layout" | "theme" | "fonts" | "hRules" | "vRules"> {
307
+ series: readonly Series[];
308
+ timeAxis: ResolvedTimeAxis;
309
+ yAxis: Required<YAxis>;
310
+ layout: ResolvedLayout;
311
+ theme: ResolvedTheme;
312
+ fonts: Required<FontOptions>;
313
+ hRules: readonly ResolvedHRule[];
314
+ vRules: readonly ResolvedVRule[];
315
+ }
316
+ /** A calendar axis whose timestamp inputs have been converted to Unix seconds. */
317
+ interface ResolvedTimeAxis extends Omit<Required<TimeAxis>, "start" | "end" | "ticks" | "minorTicks" | "majorTicks"> {
318
+ start: number | null;
319
+ end: number | null;
320
+ ticks: readonly ResolvedTick[] | null;
321
+ minorTicks: readonly number[] | null;
322
+ majorTicks: readonly number[] | null;
323
+ }
324
+ /** A label positioned at a numerical Unix timestamp. */
325
+ interface ResolvedTick {
326
+ time: number;
327
+ label: string;
328
+ }
329
+ /** Complete logical-pixel layout including the legend geometry. */
330
+ interface ResolvedLayout extends Required<Omit<Layout, "legendLayout">> {
331
+ legendLayout: Required<LegendLayout>;
332
+ }
333
+ type ThemeColorKey = "background" | "canvas" | "shadeLight" | "shadeDark" | "text" | "minorGrid" | "majorGrid" | "axis" | "arrow" | "watermark" | "frame";
334
+ /** Fully specified theme with parsed RGBA colors. */
335
+ type ResolvedTheme = Required<Omit<Theme, ThemeColorKey>> & {
336
+ [K in ThemeColorKey]: Readonly<RGBA>;
337
+ };
338
+ /** Normalized horizontal rule in original data units. */
339
+ interface ResolvedHRule {
340
+ value: number;
341
+ color: Readonly<RGBA>;
342
+ width: number;
343
+ dash: readonly [number, number] | null;
344
+ }
345
+ /** Normalized vertical rule in Unix seconds. */
346
+ interface ResolvedVRule {
347
+ time: number;
348
+ color: Readonly<RGBA>;
349
+ width: number;
350
+ dash: readonly [number, number] | null;
351
+ }
352
+ /** Recursive readonly view of plain chart configuration and metadata. */
353
+ type DeepReadonly<T> = T extends object ? {
354
+ readonly [K in keyof T]: DeepReadonly<T[K]>;
355
+ } : T;
356
+ type Canvas = HTMLCanvasElement | OffscreenCanvas;
357
+ /** BamtiGraph library version. */
358
+ declare const VERSION: string;
359
+ /** Per-operation allocation and enumeration guards. */
360
+ declare const LIMITS: Readonly<{
361
+ pixels: number;
362
+ layerPixels: number;
363
+ ticks: number;
364
+ series: number;
365
+ samples: number;
366
+ text: number;
367
+ }>;
368
+ /** Parse a supported color into integer RGBA bytes. */
369
+ declare function color(value: Color): RGBA;
370
+ /** Normalize a timestamp to Unix seconds; naive dates and invalid calendars fail. */
371
+ declare function epoch(value: Timestamp): number;
372
+ /** Copy, validate, and deeply freeze an independently timestamped series. */
373
+ declare function series(name: string, timestamps: ArrayLike<Timestamp>, values: Values, options?: SeriesOptions): Series;
374
+ /** Create a series with fixed elapsed-second spacing and copied observations. */
375
+ declare function regularSeries(name: string, values: Values, start: Timestamp, step?: number, options?: SeriesOptions): Series;
376
+ /** Daily tick presentation, without resampling observations. */
377
+ declare function daily(options?: TimeAxis | string): TimeAxis;
378
+ /** Weekly calendar-noon labels, without resampling observations. */
379
+ declare function weekly(options?: TimeAxis | string): TimeAxis;
380
+ /** Monthly tick presentation, without resampling observations. */
381
+ declare function monthly(options?: TimeAxis | string): TimeAxis;
382
+ /** Real calendar-month ticks, without resampling observations. */
383
+ declare function yearly(options?: TimeAxis | string): TimeAxis;
384
+ /** Format a value using an already validated common unit and decimal precision. */
385
+ declare function formatValue(value: number | null, unit: Unit, decimals?: number, missingText?: string): string;
386
+ /** Format a timestamp with fixed English calendar names and IANA timezone rules. */
387
+ declare function formatTime(t: Timestamp, zone?: string, format?: string): string;
388
+ /** Encode RGBA pixels as PNG with optional uncompressed UTF-8 chart metadata. */
389
+ declare function encodePNG(image: RGBAImage, metadata?: object | null): Uint8Array<ArrayBuffer>;
390
+ /** Draw image bytes without browser resampling and return the target canvas. */
391
+ declare function drawImageToCanvas(image: RGBAImage, canvas: Canvas): Canvas;
392
+ /** Download bytes in a browser; filenames may not contain path separators. */
393
+ declare function downloadBytes(bytes: Uint8Array, name: string, type?: string): void;
394
+ /** Rendered pixels and a frozen manifest; PNG encoding never requires Canvas. */
395
+ declare class RenderResult<M extends object = GraphMetadata> {
396
+ /** Pixels intentionally remain mutable; exports observe subsequent edits. */
397
+ readonly image: RGBAImage;
398
+ /** Immutable metadata captured at rendering time. */
399
+ readonly metadata: Readonly<M>;
400
+ constructor(image: RGBAImage, metadata: M);
401
+ get width(): number;
402
+ get height(): number;
403
+ get data(): RGBAImage["data"];
404
+ draw(canvas: Canvas): Canvas;
405
+ toPNG(options?: PNGOptions): Uint8Array<ArrayBuffer>;
406
+ toBlob(options?: PNGOptions): Blob;
407
+ download(filename?: string, options?: PNGOptions): void;
408
+ }
409
+ /** An immutable validated chart. Use with() to derive a changed chart. */
410
+ declare class Chart {
411
+ /** Fully normalized, deeply frozen options used by every render. */
412
+ readonly config: DeepReadonly<ResolvedChartOptions>;
413
+ constructor(options?: ChartOptions);
414
+ with(patch: ChartOptions): Chart;
415
+ render(): RenderResult;
416
+ draw(canvas: Canvas): RenderResult;
417
+ toPNG(options?: PNGOptions): Uint8Array<ArrayBuffer>;
418
+ mount(canvas: HTMLCanvasElement, options?: MountOptions): Controller;
419
+ nearest(input: Timestamp): NearestSample[];
420
+ }
421
+ /** Build the conventional inbound area and outbound line without unit conversion. */
422
+ declare function traffic(timestamps: ArrayLike<Timestamp>, inbound: Values, outbound: Values, options?: TrafficOptions): Chart;
423
+ /** Compose native chart renders and optional captions at a shared pixel scale. */
424
+ declare function dashboard(panels: readonly (DashboardPanel | Chart)[], options?: DashboardOptions): RenderResult<DashboardMetadata>;
425
+ /** Compute right-endpoint rates with exact BigInt counter subtraction. */
426
+ declare function counterRate(timestamps: ArrayLike<Timestamp>, counters: ArrayLike<number | bigint | null | undefined>, options?: CounterOptions): Samples;
427
+ /** Aggregate elapsed-time buckets, preserving gaps and explicit coverage policy. */
428
+ declare function aggregate(timestamps: ArrayLike<Timestamp>, values: Values, options?: AggregateOptions): Samples;
429
+ /** Parse quoted CSV into validated series; timestamps must strictly increase. */
430
+ declare function parseCSV(input: string, options?: CSVOptions): Series[];
431
+ /** Export the union of series timestamps, escaping formula-like text headers. */
432
+ declare function toCSV(list: readonly SeriesInput[], options?: {
433
+ escapeFormulas?: boolean;
434
+ }): string;
435
+ /** Compare RGB pixels without aligning or resizing; alpha is ignored. */
436
+ declare function compareImages(reference: RGBAImage | RenderResult<object>, actual: RGBAImage | RenderResult<object>, options?: CompareOptions): PixelDifference;
437
+ /** Visualize amplified absolute RGB differences in matching images. */
438
+ declare function differenceImage(reference: RGBAImage | RenderResult<object>, actual: RGBAImage | RenderResult<object>, amplify?: number): RenderResult<{
439
+ imageSize: [number, number];
440
+ amplify: number;
441
+ }>;
442
+ /** Copy Canvas2D pixels into independently owned RGBA storage. */
443
+ declare function readCanvas(canvas: Canvas): RGBAImage;
444
+ /** Check PNG dimensions and decode with the browser image decoder. */
445
+ declare function decodeImage(blob: Blob): Promise<RGBAImage>;
446
+ /** Return a detached copy of every chart default. */
447
+ declare function defaults(): ChartOptions;
448
+ /** Render a chart once; bitmap mode does not access browser APIs. */
449
+ declare function render(options?: ChartOptions): RenderResult;
450
+ /** Frozen default export, equivalent to the corresponding named exports. */
451
+ interface BamtiGraphAPI {
452
+ readonly VERSION: typeof VERSION;
453
+ readonly LIMITS: typeof LIMITS;
454
+ readonly Chart: typeof Chart;
455
+ readonly RenderResult: typeof RenderResult;
456
+ readonly series: typeof series;
457
+ readonly regularSeries: typeof regularSeries;
458
+ readonly traffic: typeof traffic;
459
+ readonly daily: typeof daily;
460
+ readonly weekly: typeof weekly;
461
+ readonly monthly: typeof monthly;
462
+ readonly yearly: typeof yearly;
463
+ readonly dashboard: typeof dashboard;
464
+ readonly counterRate: typeof counterRate;
465
+ readonly aggregate: typeof aggregate;
466
+ readonly parseCSV: typeof parseCSV;
467
+ readonly toCSV: typeof toCSV;
468
+ readonly compareImages: typeof compareImages;
469
+ readonly differenceImage: typeof differenceImage;
470
+ readonly encodePNG: typeof encodePNG;
471
+ readonly decodeImage: typeof decodeImage;
472
+ readonly readCanvas: typeof readCanvas;
473
+ readonly drawImageToCanvas: typeof drawImageToCanvas;
474
+ readonly downloadBytes: typeof downloadBytes;
475
+ readonly formatTime: typeof formatTime;
476
+ readonly formatValue: typeof formatValue;
477
+ readonly epoch: typeof epoch;
478
+ readonly color: typeof color;
479
+ readonly defaults: typeof defaults;
480
+ readonly render: typeof render;
481
+ }
482
+ /** The complete BamtiGraph API; importing this module creates no global. */
483
+ declare const BamtiGraph: Readonly<BamtiGraphAPI>;
484
+
485
+ export { Chart, LIMITS, RenderResult, VERSION, aggregate, color, compareImages, counterRate, daily, dashboard, decodeImage, BamtiGraph as default, defaults, differenceImage, downloadBytes, drawImageToCanvas, encodePNG, epoch, formatTime, formatValue, monthly, parseCSV, readCanvas, regularSeries, render, series, toCSV, traffic, weekly, yearly };
486
+ export type { AggregateOptions, BamtiGraphAPI, CSVColumn, CSVOptions, ChartOptions, Color, ColumnAnchors, CompareOptions, Controller, CounterOptions, DashboardMetadata, DashboardOptions, DashboardPanel, DeepReadonly, FontOptions, GraphMetadata, HRule, Interpolation, Layout, LegendLayout, LegendValues, MountOptions, NearestSample, PNGOptions, PixelDifference, RGBA, RGBAImage, ResolvedChartOptions, ResolvedHRule, ResolvedLayout, ResolvedTheme, ResolvedTick, ResolvedTimeAxis, ResolvedVRule, RuleStyle, Samples, Series, SeriesInput, SeriesOptions, Statistics, Theme, Tick, TimeAxis, Timestamp, TrafficOptions, Unit, VRule, Values, YAxis };