Skip to content

Commit 958c1ad

Browse files
committed
release: publish DeckProbe 2.3.0
Node-first JavaScript API, npm-packaged native CLI platform packages, and WASM/report fidelity fixes.
1 parent 7e92f97 commit 958c1ad

26 files changed

Lines changed: 2865 additions & 49 deletions

‎.github/workflows/ci.yml‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
run: cargo test --workspace
3030

3131
js-package:
32-
name: Browser SDK package
32+
name: npm package (CLI and browser SDK)
3333
runs-on: ubuntu-latest
3434
steps:
3535
- uses: actions/checkout@v7
@@ -38,7 +38,8 @@ jobs:
3838
targets: wasm32-unknown-unknown
3939
- uses: actions/setup-node@v4
4040
with:
41-
node-version: 20
41+
# The Vite consumer smoke test drives Vite 7, which needs >= 20.19.
42+
node-version: 20.19
4243
cache: npm
4344
cache-dependency-path: packages/deckprobe-js/package-lock.json
4445
- name: Install JavaScript dependencies
@@ -47,10 +48,12 @@ jobs:
4748
- name: Build WASM and TypeScript package
4849
working-directory: packages/deckprobe-js
4950
run: npm run build
51+
- name: Build the native CLI the launcher wraps
52+
run: cargo build --locked --release -p deckprobe
5053
- name: Install Chromium
5154
working-directory: packages/deckprobe-js
5255
run: npx playwright install --with-deps chromium
53-
- name: Run Browser SDK contract tests
56+
- name: Run contract, bundler, and CLI parity tests
5457
working-directory: packages/deckprobe-js
5558
run: npm test
5659
- name: Audit JavaScript dependencies

‎.github/workflows/release.yml‎

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,12 +287,78 @@ jobs:
287287
288288
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
289289
290-
publish-npm:
291-
name: Publish Browser SDK to npm
290+
publish-npm-platforms:
291+
name: Publish CLI platform packages to npm
292292
needs:
293293
- host
294294
if: ${{ always() && needs.host.result == 'success' }}
295295
runs-on: ubuntu-latest
296+
permissions:
297+
contents: read
298+
id-token: write
299+
steps:
300+
- uses: actions/checkout@v7
301+
with:
302+
persist-credentials: false
303+
- uses: actions/setup-node@v6
304+
with:
305+
node-version: 24
306+
registry-url: https://registry.npmjs.org
307+
package-manager-cache: false
308+
# The same archives the release serves, so the npm CLI and the shell
309+
# installers hand out byte-identical binaries.
310+
- name: Fetch dist artifacts
311+
uses: actions/download-artifact@v8
312+
with:
313+
pattern: artifacts-*
314+
path: target/distrib/
315+
merge-multiple: true
316+
- name: Verify tag and package versions
317+
working-directory: packages/deckprobe-js
318+
shell: bash
319+
run: |
320+
package_version="$(node -p "require('./package.json').version")"
321+
tag_version="${GITHUB_REF_NAME#v}"
322+
if [[ "$package_version" != "$tag_version" ]]; then
323+
echo "npm package version $package_version does not match tag $GITHUB_REF_NAME" >&2
324+
exit 1
325+
fi
326+
node ./scripts/check-version.mjs
327+
- name: Build platform packages
328+
working-directory: packages/deckprobe-js
329+
run: node ./scripts/build-platform-packages.mjs --artifacts ../../target/distrib --out npm
330+
- name: Publish platform packages
331+
working-directory: packages/deckprobe-js
332+
shell: bash
333+
env:
334+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
335+
run: |
336+
package_version="$(node -p "require('./package.json').version")"
337+
publish_tag=latest
338+
if [[ "$package_version" == *-* ]]; then
339+
publish_tag=next
340+
fi
341+
for directory in npm/*/; do
342+
name="$(node -p "require('./${directory}package.json').name")"
343+
# Publishing eight packages multiplies the failure surface, so make
344+
# a rerun after a partial failure safe instead of fatal.
345+
if npm view "$name@$package_version" version >/dev/null 2>&1; then
346+
echo "$name@$package_version is already published; skipping"
347+
continue
348+
fi
349+
npm publish "$directory" --access public --provenance --tag "$publish_tag"
350+
done
351+
352+
publish-npm:
353+
name: Publish CLI and Browser SDK to npm
354+
needs:
355+
- host
356+
# The main package declares the platform packages as optional
357+
# dependencies. Publishing it first would ship a version whose CLI cannot
358+
# resolve a binary until the rest land.
359+
- publish-npm-platforms
360+
if: ${{ always() && needs.host.result == 'success' && needs.publish-npm-platforms.result == 'success' }}
361+
runs-on: ubuntu-latest
296362
permissions:
297363
contents: read
298364
id-token: write
@@ -314,7 +380,9 @@ jobs:
314380
- name: Install Chromium
315381
working-directory: packages/deckprobe-js
316382
run: npx playwright install --with-deps chromium
317-
- name: Build and test Browser SDK
383+
- name: Build the native CLI the launcher wraps
384+
run: cargo build --locked --release -p deckprobe
385+
- name: Build and test CLI and Browser SDK
318386
working-directory: packages/deckprobe-js
319387
run: |
320388
npm run build

‎CHANGELOG.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,28 @@ All notable changes to DeckProbe are documented here.
44

55
## [Unreleased]
66

7+
## [2.3.0] - 2026-08-06
8+
9+
### Added
10+
11+
- Node.js is a first-class target for `@deckflow/deckprobe`: the `node` export
12+
condition loads WASM from disk, `probe()` initializes lazily without a custom
13+
fetch, and `probeFile(path)` returns reports whose `source_kind` matches the
14+
native CLI for the same file.
15+
- The npm package ships the native `deckprobe` CLI on PATH via per-platform
16+
optional dependencies. Release CI repackages the same cargo-dist archives that
17+
GitHub Releases serve, so `npx @deckflow/deckprobe` and the shell installers
18+
hand out byte-identical binaries.
19+
20+
### Fixed
21+
22+
- Browser and native WASM paths now report identical `confidence_score` values
23+
for the same probe.
24+
- The browser SDK exposes a stable WASM entry and guards initialization so
25+
bundlers and Workers resolve the module reliably.
26+
- Release CI coverage for the `--artifacts` platform-package packaging path
27+
(archive discovery, tar/zip extraction, and generated manifests).
28+
729
## [2.2.1] - 2026-08-04
830

931
### Fixed

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ members = [
1515
resolver = "2"
1616

1717
[workspace.package]
18-
version = "2.2.1"
18+
version = "2.3.0"
1919
edition = "2024"
2020
license = "MIT"
2121
repository = "https://github.com/deckflow/deckprobe"

‎README.md‎

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Ask for the facts you need. Get structured JSON with confidence, evidence, and m
1010
[![License: MIT](https://img.shields.io/badge/license-MIT-2f80ed.svg)](LICENSE)
1111
[![Rust 1.88+](https://img.shields.io/badge/rust-1.88%2B-orange.svg)](https://www.rust-lang.org/)
1212

13-
[Install](docs/INSTALLATION.md) · [Quickstart](#quickstart) · [Browser SDK](#browser-js-sdk) · [Execution modes](#execution-modes) · [Examples](#common-recipes) · [Formats](#supported-formats) · [CLI reference](docs/CLI-REFERENCE.md)
13+
[Install](docs/INSTALLATION.md) · [Quickstart](#quickstart) · [npm package](#javascript-package) · [Execution modes](#execution-modes) · [Examples](#common-recipes) · [Formats](#supported-formats) · [CLI reference](docs/CLI-REFERENCE.md)
1414

1515
</div>
1616

@@ -171,19 +171,43 @@ printf '%s\n' \
171171
'{"path":"deck.pptx"}' | deckprobe --jsonl -t @summary
172172
```
173173

174-
## Browser JS SDK
174+
## JavaScript package
175175

176-
The independently published `@deckflow/deckprobe` package runs the same
177-
target-driven Rust engine in WebAssembly. It accepts browser `File`, `Blob`,
178-
`ArrayBuffer`, and `Uint8Array` inputs; document bytes do not leave the
179-
browser.
176+
The independently published `@deckflow/deckprobe` package ships the `deckprobe`
177+
command for Node and runs the same target-driven Rust engine in WebAssembly for
178+
browsers and Node APIs.
180179

181-
### Install and choose an import
180+
### Install from npm
182181

183182
```sh
184183
npm install @deckflow/deckprobe
185184
```
186185

186+
That installs the `deckprobe` command as well. It is the same native binary the
187+
standalone installers ship, delivered through a per-platform optional
188+
dependency, so every flag, help page, report, and exit code is identical:
189+
190+
```sh
191+
npx @deckflow/deckprobe --help
192+
npx @deckflow/deckprobe -t slide_count deck.pptx
193+
```
194+
195+
Under Node the package also exposes `probeFile()`, which reads a file and
196+
returns the same report the CLI writes for it. Note that it holds the whole
197+
file in memory, while the CLI reads only the paths a probe needs — prefer the
198+
command, or `--jsonl` for batches, on large inputs.
199+
200+
```ts
201+
import { probeFile } from "@deckflow/deckprobe";
202+
203+
const report = await probeFile("deck.pptx", { targets: ["@summary"] });
204+
```
205+
206+
### Browser SDK
207+
208+
In the browser it accepts `File`, `Blob`, `ArrayBuffer`, and `Uint8Array`
209+
inputs; document bytes do not leave the browser.
210+
187211
| Need | Import | Use it when |
188212
|---|---|---|
189213
| Main-thread probe and discovery | `@deckflow/deckprobe` | A small, interaction-adjacent probe can use the UI thread. |
@@ -246,6 +270,27 @@ verify that the application's bundler preserves module-worker URLs. Calling
246270
the main entry point for discovery and integration tooling. See the
247271
[package guide](packages/deckprobe-js/README.md) for the complete API.
248272

273+
### Bundler setup
274+
275+
The package resolves its WebAssembly binary relative to its own JavaScript
276+
wrapper, so a bundler that relocates the wrapper without the binary breaks
277+
initialization. **Vite's dev server does this in every version** and reports
278+
either `HTTP status code is not ok` or `expected magic word 00 61 73 6d`,
279+
while `vite build` works — exclude the package from dependency
280+
pre-bundling:
281+
282+
```ts
283+
// vite.config.ts
284+
export default defineConfig({
285+
optimizeDeps: { exclude: ["@deckflow/deckprobe"] },
286+
});
287+
```
288+
289+
Main-thread-only applications can instead pass the binary URL to
290+
`initDeckProbe()` via the `@deckflow/deckprobe/wasm` export. See the package
291+
guide's [bundler notes](packages/deckprobe-js/README.md#bundlers) for both
292+
fixes, the per-version error messages, and the CDN, sub-path, and CSP cases.
293+
249294
## Execution modes
250295

251296
DeckProbe deliberately exposes different execution modes instead of treating

‎crates/deckprobe-core/src/model.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ impl Confidence {
4545
}
4646
}
4747

48-
pub fn score(self) -> f32 {
48+
/// Reported as `confidence_score`. This is `f64` rather than `f32` so the
49+
/// JSON is identical everywhere: serde_json prints the shortest round-trip
50+
/// form of an `f32` ("0.95"), but the WASM boundary widens `f32` to a JS
51+
/// number and would emit 0.949999988079071 for the same value.
52+
pub fn score(self) -> f64 {
4953
match self {
5054
Self::None => 0.0,
5155
Self::Low => 0.4,
@@ -281,7 +285,7 @@ pub struct Evidence {
281285
#[serde(skip_serializing_if = "Option::is_none")]
282286
pub value: Option<Value>,
283287
pub confidence: Confidence,
284-
pub confidence_score: f32,
288+
pub confidence_score: f64,
285289
pub path: String,
286290
pub source: String,
287291
}

‎crates/deckprobe-wasm/src/lib.rs‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,26 @@ fn options_from_js(options: JsValue) -> Result<ProbeOptions, JsValue> {
1919
})
2020
}
2121

22-
/// Probe browser-owned bytes. Engine failures are returned as schema-v2 error
22+
/// Probe host-owned bytes. Engine failures are returned as schema-v2 error
2323
/// reports; only an invalid JS options object throws.
24+
///
25+
/// `source_kind` labels where the bytes came from in the report. It defaults to
26+
/// `browser_bytes`; a Node caller that read a file from disk passes
27+
/// `local_file` so the report matches what the native CLI would have written.
2428
#[wasm_bindgen(js_name = probe)]
2529
pub fn probe_js(
2630
display_name: String,
2731
bytes: Uint8Array,
2832
options: JsValue,
33+
source_kind: Option<String>,
2934
) -> Result<JsValue, JsValue> {
3035
let options = options_from_js(options)?;
3136
let bytes = bytes.to_vec();
32-
let source = MemorySource::with_kind(display_name, "browser_bytes", bytes);
37+
let source = MemorySource::with_kind(
38+
display_name,
39+
source_kind.as_deref().unwrap_or("browser_bytes"),
40+
bytes,
41+
);
3342
match deckprobe_engine::probe_source(source, options) {
3443
Ok(report) => to_js_value(&report),
3544
Err(error) => to_js_value(&deckprobe_engine::error_report(&error)),

‎packages/deckprobe-js/.npmignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
# so the WASM artifacts listed in "files" are actually packed.
44
node_modules/
55
benchmark/
6+
# Generated platform packages are published separately, never nested here.
7+
npm/
68
scripts/serve-benchmark.mjs
79
*.tsbuildinfo
810
.DS_Store

0 commit comments

Comments
 (0)