💎 Version 7.5.1
A patch for one bug in the Weave inventory that 7.5.0 introduced, found by the first thing to consume it: api.drawn() kept listing an overlay after the plugin had cleared it away, because declarations were dropped only on a chart redraw and switching an overlay off does not cause one.
Nothing else changed, and no plugin needs to do anything differently.
| gzip | |
|---|---|
| 7.5.0 default bundle | 269,856 B |
| 7.5.1 default bundle | 269,879 B |
Both are dist/apexcharts.min.js gzipped at the default level, which is the figure npm run build prints.
Upgrading is npm install apexcharts@7.5.1.
api.drawn() reported drawings that were no longer on screen.
Declarations were cleared in one place, at the start of each draw pass, on the reasoning that layers are wiped there and repainted from state, so an inventory could never outlive what it described. That holds only for a plugin whose drawing is driven by chart renders.
An overlay plugin is not. Switching an overlay off is an interaction: it empties its layer and repaints, and no chart render happens, because going through one to remove a drawing would be an expensive way to do nothing. The declaration made for the previous paint then survived, and drawn() went on listing a trend line the viewer had just dismissed.
That is worse than the incompleteness the owner column exists to own up to. A reader can see that a list covers only the features which opted in; they cannot see that a row in it is stale.
Emptying the layer is the plugin saying, in the only way this API gives it, that it is drawing nothing. So that is now what it means:
api.on('draw', () => {
const layer = api.layer()
layer.line({ x1, y1, x2, y2, stroke: '#888' })
api.declare({ id: 'trend', label: 'Trend' })
})
// When the viewer switches the overlay off, with no redraw involved:
layer.clear() // the declaration goes with the drawing
Scoped to the plugin that cleared. Another plugin's declarations are none of its business, and a clear that emptied the whole map would let one plugin erase everyone's work from a readout.
Found by the first thing to consume drawn() rather than by review: an inventory panel that listed an overlay which had been switched off. An API with no consumer is an API whose lifecycle has not been tested, and this one was published without one.
💎 Version 7.5.0
A minor release for plugin authors. Weave goes to API v6 with four additions, and the theme running through all of them is the same: the chart knew something a plugin had no way to ask for, so the plugin either guessed, or reached for the caller's config, or did without.
The largest of the four ends a pattern this library had been quietly asking plugins to use. Options indexed by series position have no per-series escape hatch, so a plugin wanting one value for its own series had to write the array covering every series and put the caller's back afterwards. api.claim() replaces that with something the host resolves at the point it reads the option, so nothing is written at all.
Everything here is additive. No plugin needs to declare apiVersion: 6 to keep working, and a plugin that wants the new surface while still running on older hosts can now ask for it by name.
| gzip | |
|---|---|
| 7.4.0 default bundle | 268,612 B |
| 7.5.0 default bundle | 269,856 B |
Both measured the same way, gzip at its default level over dist/apexcharts.min.js, so they are comparable with each other. Earlier release notes quoted a figure from the CI build, which lands a few hundred bytes apart from the committed bundle.
No API breaking changes. Upgrading is npm install apexcharts@7.5.0.
The version integer never answered the question a plugin actually has, which is "does this host have X". A plugin declaring a version newer than the host is skipped outright, so one that supports several hosts declares the lowest version it can run on and then has to discover anything above that. Until now it did so by sniffing the facade for function members, which means every plugin reimplements the same guesswork against a surface it is deliberately not supposed to know the shape of.
if (api.can('claim')) {
// ...
}
api.capabilities is the whole list, for logging and support. Names describe the capability rather than the version that introduced it, and are permanent once published: removing one is a breaking change on the same terms as removing a member.
Each name is probed off the facade that was actually built rather than copied from a constant, so a member that is ever made conditional drops out of the list instead of being advertised and then missing. The list is an array with the lookup behind a closure rather than a frozen Set, because Object.freeze does not touch a Set's internal slots: add() and delete() would go on working, and one plugin could quietly edit what every later plugin is told.
scales is deliberately not a capability. It is null for non-axis charts, which is a fact about the chart rather than about the host, and advertising it would tell a plugin on a pie chart that projection is available.
stroke.dashArray and dataLabels.enabledOnSeries are indexed by series position. There is no per-series form of either, so a plugin that adds a computed series and wants it dashed, or wants the chart to stop printing labels over it, has had to write the array covering every series including the caller's, then restore what it found.
7.4.0 added api.info.stroke so a plugin could at least see what to restore, which made the pattern survivable rather than sound. Four things are wrong with it. The restore is stale the moment the caller calls updateOptions. It leaks if the plugin throws in between. A caller who wrote a single number gets it flattened into an array. And two plugins doing it at once fight, with the winner decided by execution order.
const claim = api.claim('stroke.dashArray', [
{ series: 'Revenue (forecast)', value: 6 },
])
claim.release()
A claim says what the plugin wants, and the host answers with it where the option is READ. Nothing is written, so there is nothing to restore, releasing is a deletion, and a caller's own updateOptions composes with the claim rather than reverting it. Claims resolve in order, so two plugins on one series produce a defined winner instead of whichever happened to run last.
Name the series rather than its position where you can. A name is resolved each time the option is read, so the claim follows that series when the caller adds, removes or reorders others.
Claimable options are an allowlist, stroke.dashArray and dataLabels.enabledOnSeries to begin with, each declaring its value type so a wrong one is dropped rather than drawn. An option that is not on the list returns null rather than throwing, so a plugin built against a newer host degrades instead of breaking. Every claim is released on teardown, on destroy, and if the host disables the plugin after repeated errors, so none can outlive the plugin holding it.
A plugin that wants to list what a chart is showing had no way to learn it. It knows its own overlays and can read the series off api.data, but the caller's annotations, another plugin's overlays and the ink strokes a viewer drew were all invisible to it. A layers panel and an export summary are the same question, and both were unanswerable.
api.drawn() // [{ id, kind, label, owner, visible }, ...]
api.declare({ id: 'trend-1', label: 'Trend' })
drawn() reports the series, the caller's annotations, and whatever plugins have declared, each entry naming its owner. The owner is the point rather than decoration: the list is only ever as complete as the features that opted into it, so a reader can say what its inventory covers instead of presenting a partial list as everything.
declare() is how a plugin joins, called from its draw handler. Declarations are cleared with the layers at the start of every draw, so an inventory can never outlive the drawing it describes, and declaring the same id twice replaces it rather than growing a duplicate row.
Read only, deliberately. Removing or hiding another feature's output is a much larger promise than this platform makes: it would mean one plugin reaching into another's state with no way for the owner to refuse.
One limitation worth knowing. Annotations do not carry a caller id. The id on a point annotation is the key of a deferred-execution entry rather than a handle on the drawing, so entries fall back to annotation:<type>:<index> and take their label from label.text where the caller wrote one.
Two small reads.
api.info.title is what the chart calls itself. A plugin that has to name this chart to somebody, a page-level readout listing several of them, otherwise heads each row with the container's id, which is a string written for a stylesheet rather than for a reader. The title is the name the page already chose and already shows. It is an empty string rather than undefined for an untitled chart, so it drops into a template without a guard.
api.info.title // 'Revenue by region', or ''
modifiers on the pointer payload reports the keys held during the interaction. Shift-click to add to a selection is the gesture that page-level coordination wants and could not express, because the keys exist only on the DOM event, which the pointer handler had been discarding.
api.pointer((e) => {
if (e.modifiers.shift) { /* add to the selection */ }
})
Always the same four booleans, shift, ctrl, alt and meta, never partial and never undefined, because a plugin writes e.modifiers.shift inside a viewer's click and a sometimes-missing key is how that becomes a crash. All four are false where there was no DOM event, which is the honest answer for a keyboard or programmatic selection.
Two changes, both generated from one template shared across the organisation.
Products with no free tier no longer print the Community sections. Their files told a reader under $2M in revenue that they could use the product for free, and said it in five separate places: the dual-license opening, the Community section itself, the non-profit budget branch, the line about work built only from free features, and the acceptance list. The runtime never agreed: those products check the licence over the whole product, so no key means a watermark whatever the customer earns.
Products that do have a free tier now say what is in it. The Community section described who qualifies, on revenue, non-profit status or educational use, and never what you actually get. ApexCharts and the others with a free tier now list which features are free and which need Premium, taken from the same lists the pricing page renders.
The Weave feature module is now 4.48 KB gzipped on top of core, up about 1.2 KB in this release, against a Tier-1 budget of roughly 5 KB. That was measured by building the default bundle with and without it, which the build reports as 263.53 KB and 259.05 KB gzipped, because the budget test checks Tier-1 membership rather than bytes. Worth measuring again before the next addition rather than assuming the headroom is still there.
Release Next v3.0.0-next.14
- #827 by @bobsingor – Describe measurement annotation fields and add typed APIs to read page viewports and update page calibration.
-
#827 by @bobsingor – Expose page measurement calibration and viewport reads in the cloud engine, and deliver viewport change events with document version coherence.
-
#800 by @LazyCompiler – Cloud-backed search honours the
ignoreWhitespacequery flag, which travels inside the search token, and search cursors are pinned to it: replaying a cursor minted with the flag against a query without it (or vice versa) is rejected withInvalidArg, matching the local engine.
- #827 by @bobsingor – Add generated page viewport and scale APIs and measurement annotation types.
-
#827 by @bobsingor – Persist page measurement calibration in layer artifacts with authorization and audit events. Calibration advances the document version while retaining existing page cache versions and annotation scales.
-
#800 by @LazyCompiler – The layer search routes (
/v1/docs/:docId/layers/:layerName/search/{rects,full}/data) acceptignoreWhitespace=truealongside the other query flags and forward it to the engine, so a cloud search forinvoicefinds a letter-spacedi n v o i c e. Combining it withregex=trueis rejected withInvalidArg, and the flag is carried by the search tokens that page through results.
-
#827 by @bobsingor – Add distance drawing with separate endpoint and leader placement, four geometry handles, and directly draggable captions. Keep previews, hit testing, and selection bounds aligned, including short dimensions with outside arrows and displaced-label connectors. Measurement labels use the engine's PDF-coordinate rounding rules.
Rotate measurements around the center of their complete oriented selection frame, including leaders and displaced captions. Use the same frame for pointer rotation, quarter turns, reset, selection, and hit testing, keeping the center stable after a saved appearance is reloaded. Attach the rotation handle to the selection border without extra measurement-specific spacing.
Add perimeter and area creation through the existing polyline and polygon gestures, live labels, direct caption dragging, and complete selection bounds. Carry manual PDF-space caption centers through whole-shape transforms while vertex edits leave them fixed. Reject invalid area creation and vertex edits.
-
#827 by @bobsingor – Add measurement DTOs, page calibration contracts, and shared distance, perimeter, area, and number-format helpers. Caption positions use PDF coordinates and support partial updates and explicit resets.
Report invalid geometry for crossing, overlapping, or degenerate area boundaries while accepting either winding and an explicit closing vertex.
-
#800 by @LazyCompiler – Add the
ignoreWhitespaceflag toSearchQuery. A literal query folded with it drops whitespace on both sides instead of collapsing it, soinvoicefinds the letter-spacedi n v o i c ethat OCR'd scans and tracked-out headings produce, andtotal amountfindstotalamount. Hits still span the original text including the dropped whitespace, andwholeWordboundaries are checked on the original text. The flag is literal-only —validateSearchQueryrejects it together withregex(ignore-whitespace-with-regex) — and it round-trips through search tokens.foldTextgains the matchingdropWhitespaceoption, and the shared search conformance suite covers the flag.
- #827 by @bobsingor – Support distance, perimeter, and area measurements through the local engine. Add page measurement viewport reads and calibration writes, with persistence in saved PDFs and layers and viewport change events.
-
#827 by @bobsingor – Read and write PDF measurement dictionaries and viewports, derive labels on geometry or scale edits, and preserve imported labels on style edits. Move manual shape captions with rigid geometry transforms and validate measurement input before native writes.
Avoid an additional full-document buffer copy when exporting a saved PDF.
-
#800 by @LazyCompiler – Local engines honour the
ignoreWhitespacesearch flag: a query carrying it re-folds the cached page text with whitespace dropped, soinvoicefinds a letter-spacedi n v o i c e, and search cursors key on the flag so a resumed search never mixes hits from the two folds. Combining the flag withregexis rejected withInvalidArg.
- #827 by @bobsingor – Add the measurement feature entry with reactive calibration, page-scale, and readout hooks. Render rotated distance captions and use the standard square handles for measurement endpoints and leaders during annotation gestures.
-
#827 by @bobsingor – Add distance and calibration presets, point-based viewport scale selection, and per-annotation recalculation reports. Persist leader and caption edits in native PDF fields, preserve the measured endpoints during offset edits, and keep derived measurement values read-only in comments.
Use the standard selection spacing for measurement annotations.
Keep locally created and edited measurements vector-rendered after the engine saves their appearance, matching the existing annotation lifecycle and avoiding repeated switches to raster rendering.
Add area and perimeter presets with scale snapshots captured at the first vertex. Persist shape captions and derived values through ordinary annotation edits, retaining vector rendering after engine responses.
-
#827 by @bobsingor – Introduce page calibration, scale presets, units, precision, measurement readouts, and optional recalculation of existing annotations. Report partial results across pages and support session-only calibration on older engines.
Expose area unit choices and independent area-unit updates that preserve distance formatting.
-
#827 by @bobsingor – Add a Measure toolbar with distance drawing, a scale sidebar, and known-length calibration controls. Support Escape to cancel an unfinished drawing, with English and Spanish labels.
Use distinct calibration and scale-setting icons that inherit the viewer's theme and active-state colors.
Add Perimeter and Area tools, dynamic stroke-colored measurement icons across toolbar and cursor, separate area-unit controls, selected area/perimeter readouts, and a reset-label-position action.
- #827 by @bobsingor – Export MeasurementToken through every viewer entry point so applications can access page calibration and measurement controls through the viewer handle.
6.6.5
- 🐞 Fix numeric
0content rendering across Result, message, notification, Avatar, Modal, Descriptions, and Form.Item. #59153 #59125 #59289 @bhumin18 @nrps9909 @QDyanbing - Upload
- 🐞 Fix Upload.Dragger custom
style.heightbeing overridden when theheightprop is not set. #59319 @dogledogle - ♿ Fix Upload file names being focusable as buttons when no preview action is available. #59295 @QDyanbing
- 🐞 Fix Upload.Dragger custom
- Transfer
- 🐞 Fix Transfer calling a stale
onSelectChangecallback after it is replaced or removed. #59307 @yunfeizhu - 🐞 Fix Transfer
footercallbacks not receivingdirectionwhen using rest parameters. #59303 @QDyanbing
- 🐞 Fix Transfer calling a stale
- 🐞 Fix Avatar not retrying image loading after
srcSetchanges. #59297 @QDyanbing - 🐞 Fix Anchor scrolling and Table and Transfer range selection using stale values after updates. #59308 @QDyanbing
- 🐞 Fix Select inconsistent single and multiple heights after customizing global
fontSizeorlineHeight. #59298 @zombieJ - 🤖 Fix Tooltip, Popover, Popconfirm, and Slider TypeScript definitions accepting unsupported rc Tooltip props. #59288 @QDyanbing
- 🛎 Fix Drawer not warning that
destroyOnCloseis deprecated. #59299 @dogledogle
- 🐞 修复 Result、message、notification、Avatar、Modal、Descriptions 和 Form.Item 无法正确渲染数值
0内容的问题。#59153 #59125 #59289 @bhumin18 @nrps9909 @QDyanbing - Upload
- 🐞 修复 Upload.Dragger 未设置
height属性时自定义style.height被覆盖的问题。#59319 @dogledogle - ♿ 修复 Upload 没有可用预览操作时文件名仍可作为按钮聚焦的问题。#59295 @QDyanbing
- 🐞 修复 Upload.Dragger 未设置
- Transfer
- 🐞 修复 Transfer 在替换或移除
onSelectChange后仍调用旧回调的问题。#59307 @yunfeizhu - 🐞 修复 Transfer 使用剩余参数的
footer回调无法获取direction的问题。#59303 @QDyanbing
- 🐞 修复 Transfer 在替换或移除
- 🐞 修复 Avatar 图片加载失败后更新
srcSet无法重新加载的问题。#59297 @QDyanbing - 🐞 修复 Anchor 滚动及 Table 和 Transfer 范围选择在更新后仍使用旧值的问题。#59308 @QDyanbing
- 🐞 修复 Select 自定义全局
fontSize或lineHeight后单选与多选高度不一致的问题。#59298 @zombieJ - 🤖 修正 Tooltip、Popover、Popconfirm 和 Slider 的 TypeScript 类型定义,避免接受实际无效的 rc Tooltip 属性。#59288 @QDyanbing
- 🛎 修复 Drawer 未提示
destroyOnClose已废弃的问题。#59299 @dogledogle
v2.16.0
- A fallback codec class passed as
CompressionStreamFallbackorDecompressionStreamFallbackcan declare two static flags, now documented onCompressionStreamLikeandDecompressionStreamLike.supportedFormatslists the formats the class supports, e.g.["deflate-raw", "gzip"], and the library reads it instead of probing a format by constructing the class.requiresModule, whentrue, says the class cannot be constructed before the module of the worker is ready, the WebAssembly module of zip.js or the module loaded by theinitfunction passed toinitWorker: the library then waits for that module, uses the native codec instead when the module fails to load, and opens a compression class as"gzip"to read the CRC-32 of the data from the trailer, so such a class must support that format
- With
checkCrc32: true, the WebAssembly codec and the nativeDecompressionStreamverify the CRC-32 while inflating, so the separate pass over the output on the JavaScript thread is gone for those two codecs. On the WebAssembly codec the check costs about 7 ms per 20 MB instead of 15, and reading a 20 MB text entry takes 53 ms instead of 62 ms on Node.js; on Node'sDecompressionStreamthe pass was already overlapped by the threadpool and the gain is within the noise. Entries compressed with deflate64 or a registered codec, AES entries whose CRC-32 is not stored, and the pure-JavaScript zlib port keep the separate pass. The mechanism is the one the writer has used since 2.15.0: the raw deflate data is framed as gzip with a trailer holding the CRC-32 and size declared by the entry, which the inflater checks HttpReaderwithcombineSizeEocd: true, the option, off by default, that fetches the last 64 KB of the archive with the request that reads its size, now serves the entry data lying in those bytes from that response. Reading an archive shorter than 64 KB costs that one request, where each entry used to issue a range request of its own, and in a larger archive an entry stored in its last 64 KB costs no extra request. WithuseXHR: truethe reader already behaved that way
- On a host whose
DecompressionStreamlacks"deflate-raw", Chromium 80 to 102 and Node.js 18 for instance, an entry with a corrupted CRC-32 read withcheckCrc32: falsewas delivered as if it were valid, and an entry whose stored uncompressed size was wrong failed only after a 5 second watchdog. The gzip decoder of such a host checks the CRC-32 and size in the trailer it is given regardless, so the verification is free: a corrupted entry now fails withERR_INVALID_CRC32whatevercheckCrc32says, and a wrong size fails withERR_INVALID_UNCOMPRESSED_SIZEas soon as the output has been read - With
useCompressionStream: falseand aCompressionStreamFallbackorDecompressionStreamFallbackwhose constructor throws, the entry now fails with that error. It used to be served silently by the gzip format of the native codec, souseCompressionStream: falsewas not honoured
BENCHMARKS.mdstates the cost of the CRC-32 check on read: about 7 ms per 20 MB on the WebAssembly codec, within the noise on Node'sDecompressionStream, and about 15 ms on the pure-JavaScript zlib port, which keeps a separate pass
- New tests pin the gzip trailer on the inflate side, with the corrupted CRC-32 and size errors on the WebAssembly and native codecs and the raw route with the check off, the end of archive cache of
HttpReaderwith the request counts of a small archive and of an entry in the cached tail, the propagation of a fallback codec failure, the CRC-32 and size verification on a host without"deflate-raw", and the formats such a host is asked for on the deflate and inflate sides - The continuous integration runs on Ubuntu 26.04, and the reader stream release test allows 30 s per step instead of 5, a test timeout only, which the Firefox 102 lane of the native build exceeded under load in three of four runs
Full Changelog: https://github.com/gildas-lormeau/zip.js/compare/v2.15.0...v2.16.0
Co-Authored-By: Claude Fable 5.1 noreply@anthropic.com
v16.4.0-canary.37
- Preserve closed-parameter restrictions in client route prediction: #98889
- [test] Fix deployment tests that relied on implicit startup: #98935
- Turbopack: Add support for specifying additional roots: #98003
- Turbopack: Add symlinks and additional roots to NFT metadata: #98469
Huge thanks to @gnoff, @unstubbable, and @bgw for helping!
formatjs_cli: 1.7.4
1.7.4 (2026-09-19)
- macOS Apple Silicon:
formatjs_cli-darwin-arm64 - Linux ARM64:
formatjs_cli-linux-arm64 - Linux x86_64:
formatjs_cli-linux-x64 - Windows x64:
formatjs_cli-win32-x64.exe
# macOS (Apple Silicon)
curl -LO https://github.com/formatjs/formatjs/releases/download/formatjs_cli_v1.7.4/formatjs_cli-darwin-arm64
chmod +x formatjs_cli-darwin-arm64
sudo mv formatjs_cli-darwin-arm64 /usr/local/bin/formatjs
# Linux
curl -LO https://github.com/formatjs/formatjs/releases/download/formatjs_cli_v1.7.4/formatjs_cli-linux-x64
chmod +x formatjs_cli-linux-x64
sudo mv formatjs_cli-linux-x64 /usr/local/bin/formatjs
# Linux ARM64
curl -LO https://github.com/formatjs/formatjs/releases/download/formatjs_cli_v1.7.4/formatjs_cli-linux-arm64
chmod +x formatjs_cli-linux-arm64
sudo mv formatjs_cli-linux-arm64 /usr/local/bin/formatjs
# Windows x64 (PowerShell)
curl.exe -LO https://github.com/formatjs/formatjs/releases/download/formatjs_cli_v1.7.4/formatjs_cli-win32-x64.exe
Verify the checksums:
curl -LO https://github.com/formatjs/formatjs/releases/download/formatjs_cli_v1.7.4/checksums.txt
shasum -a 256 -c checksums.txt
eslint-plugin-formatjs: 8.0.5
8.0.5 (2026-09-19)
- The following workspace dependencies were updated
- dependencies
- @formatjs/icu-messageformat-parser bumped to 3.5.20
- @formatjs/ts-transformer bumped to 4.4.22
- dependencies
react-intl: 12.1.2
12.1.2 (2026-09-19)
- The following workspace dependencies were updated
- dependencies
- @formatjs/icu-messageformat-parser bumped to 3.5.20
- @formatjs/intl bumped to 6.1.2
- intl-messageformat bumped to 12.1.2
- dependencies
@formatjs/unplugin: 1.2.12
1.2.12 (2026-09-19)
- The following workspace dependencies were updated
- dependencies
- @formatjs/icu-messageformat-parser bumped to 3.5.20
- @formatjs/ts-transformer bumped to 4.4.22
- dependencies