chore(release): bump version to 0.8.2
This commit is contained in:
parent
70e202d802
commit
a1e7151f4b
54
Cargo.lock
generated
54
Cargo.lock
generated
|
|
@ -3143,7 +3143,7 @@ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
|||
|
||||
[[package]]
|
||||
name = "op-acp"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"op-ai",
|
||||
|
|
@ -3157,11 +3157,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-ai"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
|
||||
[[package]]
|
||||
name = "op-ai-skills"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"include_dir",
|
||||
"jian-ops-schema",
|
||||
|
|
@ -3170,7 +3170,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-cli"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"jian-ops-schema",
|
||||
|
|
@ -3186,7 +3186,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-codegen"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"jian-ops-schema",
|
||||
|
|
@ -3199,7 +3199,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-config-store"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -3207,7 +3207,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-design-lint"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"jian-ops-schema",
|
||||
"serde",
|
||||
|
|
@ -3216,7 +3216,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-editor-core"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"jian-core",
|
||||
|
|
@ -3231,7 +3231,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-editor-host-core"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"jian-ops-schema",
|
||||
"op-ai",
|
||||
|
|
@ -3245,7 +3245,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-editor-ui"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"base64",
|
||||
|
|
@ -3263,7 +3263,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-figma"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"flate2",
|
||||
|
|
@ -3278,7 +3278,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-git"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"git2",
|
||||
"op-config-store",
|
||||
|
|
@ -3289,7 +3289,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-host-desktop"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_macos",
|
||||
|
|
@ -3346,7 +3346,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-host-native"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"casement",
|
||||
|
|
@ -3375,7 +3375,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-host-services"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"agent",
|
||||
"anthropic-agent-sdk",
|
||||
|
|
@ -3412,7 +3412,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-host-web"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"base64",
|
||||
|
|
@ -3439,7 +3439,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-host-web-server"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"op-host-services",
|
||||
"serde_json",
|
||||
|
|
@ -3447,7 +3447,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-html"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"encoding_rs",
|
||||
|
|
@ -3462,11 +3462,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-i18n"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
|
||||
[[package]]
|
||||
name = "op-mcp"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"jian-ops-schema",
|
||||
"jian-scene",
|
||||
|
|
@ -3484,7 +3484,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-opmerge"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"jian-ops-schema",
|
||||
"serde_json",
|
||||
|
|
@ -3493,7 +3493,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-orchestrator"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"jian-ops-schema",
|
||||
|
|
@ -3512,7 +3512,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-pen-loader"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"jian-core",
|
||||
"jian-ops-schema",
|
||||
|
|
@ -3527,14 +3527,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-process-io"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "op-rpc-transport"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -3542,7 +3542,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-smoke"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"agent",
|
||||
"futures",
|
||||
|
|
@ -3561,7 +3561,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "op-web-sdk"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"jian-ops-schema",
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ exclude = [
|
|||
default-members = ["crates/op-host-desktop", "crates/op-cli", "crates/op-host-web-server"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
edition = "2021"
|
||||
# 1.87 floor: op-mcp's `script` feature (enabled unconditionally by
|
||||
# op-host-services + op-orchestrator) depends on rquickjs 0.12, whose
|
||||
|
|
|
|||
95
RELEASE_NOTES/v0.8.2.md
Normal file
95
RELEASE_NOTES/v0.8.2.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# OpenPencil v0.8.2
|
||||
|
||||
`v0.8.2` is a pre-release update focused on import fidelity, large-document
|
||||
performance, safer persistence, and backward-compatible OpenPencil files.
|
||||
|
||||
## Highlights
|
||||
|
||||
- Figma imports preserve substantially more authored structure and appearance,
|
||||
including component instances and swaps, masks, blend modes, page
|
||||
backgrounds, image tiling, vectors, text, child order, and absolute layout.
|
||||
- Import preparation, image resolution, text measurement, and component lookup
|
||||
reuse cached work, while cancellation and background conversion keep the
|
||||
desktop UI responsive.
|
||||
- Imported desktop files can be written as adjacent `.op` documents. Existing
|
||||
targets require confirmation; choosing not to replace creates a numbered
|
||||
copy.
|
||||
- Large `.op` files use memory-mapped input and a fast Preserve-mode loader.
|
||||
Only the active page scene is built for editing, and the previous page scene
|
||||
is released when switching pages.
|
||||
- Page switches automatically fit and center the newly active page.
|
||||
- Save and Save As run through serialized background jobs. Clean bound
|
||||
documents can stream their source bytes while rewriting only editor
|
||||
metadata, avoiding a full document clone.
|
||||
- Legacy `.op` files are detected after compatibility repair and can be
|
||||
rewritten in the current format, saved as a numbered copy, or opened without
|
||||
conversion.
|
||||
- HTML import accepts complete pages, multi-file projects, and ZIP packages
|
||||
with local CSS, images, nested imports, and common text encodings.
|
||||
|
||||
## Figma Fidelity
|
||||
|
||||
- Component properties and instance overrides retain component backing data,
|
||||
swap targets, authored geometry, and style references.
|
||||
- Alpha, vector, and luminance masks render through the shared native and web
|
||||
paint paths.
|
||||
- Layer and fill blend modes, image tile scale, and page background color are
|
||||
represented in the schema and editable in the property inspector.
|
||||
- Boolean fallbacks, vector bounds, image fills, text metrics, layout
|
||||
positioning, and paint ordering more closely match the source Figma file.
|
||||
- The generated TypeScript schema bindings and web SDK types remain compatible
|
||||
with older optional fields.
|
||||
|
||||
## Persistence and Compatibility
|
||||
|
||||
- The current `.op` format marker is `1.2`.
|
||||
- Existing current-format documents do not trigger migration solely because
|
||||
optional editor metadata is absent.
|
||||
- Legacy conversion preserves unknown top-level and nested fields, handles
|
||||
deeply nested documents with stack protection, and writes through an atomic
|
||||
sibling temporary file.
|
||||
- Before replacing an original document, OpenPencil verifies that the source
|
||||
has not changed since it was opened. A concurrent external change falls back
|
||||
to a numbered copy.
|
||||
- Failed migration or save publication does not bind stale in-memory state to
|
||||
the target path.
|
||||
|
||||
## Performance
|
||||
|
||||
- Figma preparation caches decoded values, text measurements, image lookups,
|
||||
instance assignments, and component style references.
|
||||
- Desktop document loading maps the source file instead of first copying the
|
||||
entire file into a second byte buffer.
|
||||
- Preserve-authored documents can skip the generic full JSON compatibility DOM
|
||||
when the fast loader proves the current shape is safe.
|
||||
- Scene construction is active-page scoped across desktop, web, MCP, image
|
||||
search, and export consumers.
|
||||
- Large import and save payloads use indirection or streaming paths to reduce
|
||||
queue size and peak allocations.
|
||||
|
||||
## Web and SDK
|
||||
|
||||
- Web Figma conversion stages output through a worker-backed temporary writer
|
||||
instead of retaining every generated byte in the main UI task.
|
||||
- Web save requests are queued and coalesced so an older serialization cannot
|
||||
publish after a newer edit.
|
||||
- Page fitting, compositing properties, masks, image tiling, and editor metadata
|
||||
round-trip through the web host.
|
||||
- `@zseven-w/op-web-sdk`, `@zseven-w/op-web-sdk-react`, and
|
||||
`@zseven-w/op-web-sdk-vue` are synchronized at `0.8.2`.
|
||||
|
||||
## Release Artifacts
|
||||
|
||||
- Native desktop installers and standalone `op` CLI archives for macOS,
|
||||
Windows, and Linux.
|
||||
- `ghcr.io/zseven-w/openpencil-web:v0.8.2` for the Rust web host.
|
||||
- Web SDK packages under the npm `next` tag, with package tarballs attached to
|
||||
the GitHub prerelease.
|
||||
|
||||
## Compatibility Notes
|
||||
|
||||
- The public web SDK remains a read-only viewer surface.
|
||||
- Active-page scene loading reduces renderer memory, but the canonical document
|
||||
still retains all page data while open.
|
||||
- Future incompatible format majors are rejected instead of being rewritten.
|
||||
- The retired TypeScript application workspace is not part of this release.
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
//! Account / sign-in state for the v0.8.2 user system (platform +
|
||||
//! zseven-sso). The real backend is not live yet — this module carries
|
||||
//! Account / sign-in state for the planned platform + zseven-sso user
|
||||
//! system. The real backend is not live yet — this module carries
|
||||
//! the state model plus a dev-only fake-login seam so the topbar avatar
|
||||
//! button, its dropdown, the sign-in modal, and the settings modal's
|
||||
//! Account tab can all be built and exercised end-to-end before the
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ pub enum AgentSettingsTab {
|
|||
Images,
|
||||
Fonts,
|
||||
System,
|
||||
/// Sign-in status + workspace identity (v0.8.2 user system).
|
||||
/// Sign-in status + workspace identity for the planned user system.
|
||||
Account,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1096,7 +1096,7 @@ pub struct EditorUiState {
|
|||
/// settings-modal input.
|
||||
pub settings_input: jian_core::text_input::TextInputState,
|
||||
|
||||
// --- Account (v0.8.2 user system: platform + zseven-sso) --------
|
||||
// --- Account (planned platform + zseven-sso user system) -------
|
||||
/// Signed-in / signed-out identity. The backend (OIDC Auth Code +
|
||||
/// PKCE via system browser) is not wired yet — see
|
||||
/// `AccountState::dev_fake_signed_in` for the dev-only fast path.
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
//! authentication focused: OpenPencil identity, one browser action,
|
||||
//! and a concise security/status note.
|
||||
//!
|
||||
//! The real auth flow (v0.8.2 M3: OIDC Auth Code + PKCE via system
|
||||
//! browser) is not wired yet, so the primary button is an honest stub:
|
||||
//! The planned auth flow (OIDC Auth Code + PKCE via the system browser)
|
||||
//! is not wired yet, so the primary button is an honest stub:
|
||||
//! it stays clickable, but production builds reveal a "coming soon"
|
||||
//! note instead of pretending to sign in. `AccountState::
|
||||
//! dev_fake_signed_in` is the dev/demo fast path (gated host-side by
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ pub(super) const GIT_BUTTON_AVAILABLE: bool = !cfg!(target_arch = "wasm32");
|
|||
/// has no equivalent runtime/measurement bridge, so hide the button there
|
||||
/// rather than exposing a non-interactive preview flag.
|
||||
pub(super) const PREVIEW_BUTTON_AVAILABLE: bool = !cfg!(target_arch = "wasm32");
|
||||
/// The v0.8.2 sign-in flow (OIDC Auth Code + PKCE via the system
|
||||
/// The planned sign-in flow (OIDC Auth Code + PKCE via the system
|
||||
/// browser) is desktop-only. The web build has no equivalent, so the
|
||||
/// avatar button (and its layout slot) is compiled out there rather
|
||||
/// than painting a click that goes nowhere (same pattern as
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ mod tests {
|
|||
#[test]
|
||||
fn visits_every_typed_image_source_shape_used_by_imports() {
|
||||
let document = serde_json::from_value(serde_json::json!({
|
||||
"version": "0.8.2",
|
||||
"version": "1.0.0",
|
||||
"children": [
|
||||
{
|
||||
"type": "rectangle",
|
||||
|
|
@ -305,7 +305,7 @@ mod tests {
|
|||
let png_src = "data:image/png;base64,U0FNRQ==";
|
||||
let jpeg_src = "data:image/jpeg;base64,U0FNRQ==";
|
||||
let document = serde_json::from_value(serde_json::json!({
|
||||
"version": "0.8.2",
|
||||
"version": "1.0.0",
|
||||
"children": [
|
||||
{"type": "image", "id": "png", "src": png_src},
|
||||
{"type": "image", "id": "jpeg", "src": jpeg_src}
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ fn deferred_thumbnail_binding_uses_the_final_source_string() {
|
|||
let final_src = format!("data:image/jpeg;base64,{}", B64.encode(final_bytes));
|
||||
let provisional_src = format!("data:image/png;base64,{}", B64.encode(provisional));
|
||||
let doc = serde_json::from_value(serde_json::json!({
|
||||
"version": "0.8.2",
|
||||
"version": "1.0.0",
|
||||
"children": [{
|
||||
"type": "rectangle",
|
||||
"id": "image-fill",
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ impl WidgetHostNative {
|
|||
}
|
||||
LoginModalHit::SignIn => {
|
||||
// Dev/demo fast path — never reachable in a production
|
||||
// build; the real flow is `v0.8.2 M3: OIDC Auth Code +
|
||||
// PKCE via system browser`.
|
||||
// build; the planned real flow uses OIDC Auth Code +
|
||||
// PKCE via the system browser.
|
||||
if dev_fake_login_enabled() {
|
||||
self.editor_state.editor_ui.account =
|
||||
op_editor_core::AccountState::dev_fake_signed_in();
|
||||
|
|
|
|||
|
|
@ -288,8 +288,8 @@ impl WidgetHost {
|
|||
}
|
||||
AgentSettingsHit::OpenLoginModal | AgentSettingsHit::SignOutAccount => {
|
||||
// Unreachable: `AgentSettingsPanelMode::WebBuiltinOnly`
|
||||
// omits the Account tab from `visible_tabs` (the v0.8.2
|
||||
// sign-in flow is desktop-only), so `hit_test` never
|
||||
// omits the Account tab from `visible_tabs` (the sign-in
|
||||
// flow is desktop-only), so `hit_test` never
|
||||
// resolves to these variants on the web build.
|
||||
}
|
||||
AgentSettingsHit::FocusMcpPort => {
|
||||
|
|
|
|||
|
|
@ -516,7 +516,7 @@ impl WidgetHost {
|
|||
TopBarHit::Account => {
|
||||
// Unreachable: `ACCOUNT_BUTTON_AVAILABLE` gates the
|
||||
// avatar button out of the web build's hit-test (the
|
||||
// v0.8.2 sign-in flow is desktop-only).
|
||||
// sign-in flow is desktop-only).
|
||||
}
|
||||
}
|
||||
self.mark_dirty();
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
},
|
||||
"op-web-sdk": {
|
||||
"name": "@zseven-w/op-web-sdk",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"devDependencies": {
|
||||
"jsdom": "^25.0.0",
|
||||
"tsup": "^8.0.0",
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
},
|
||||
"op-web-sdk-react": {
|
||||
"name": "@zseven-w/op-web-sdk-react",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"dependencies": {
|
||||
"@zseven-w/op-web-sdk": "workspace:*",
|
||||
},
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
},
|
||||
"op-web-sdk-vue": {
|
||||
"name": "@zseven-w/op-web-sdk-vue",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"dependencies": {
|
||||
"@zseven-w/op-web-sdk": "workspace:*",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@zseven-w/op-web-sdk-react",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"description": "React adapter for the OpenPencil read-only web viewer SDK",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export const VERSION = '0.8.1';
|
||||
export const VERSION = '0.8.2';
|
||||
export { DesignProvider, useViewer } from './use-viewer.js';
|
||||
export { useDocument, useViewport, useActivePage } from './hooks.js';
|
||||
export { DesignView, type DesignViewProps } from './design-view.js';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@zseven-w/op-web-sdk-vue",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"description": "Vue 3 adapter for the OpenPencil read-only web viewer SDK",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export const VERSION = '0.8.1';
|
||||
export const VERSION = '0.8.2';
|
||||
export { viewerKey } from './injection.js';
|
||||
export { provideViewer, provideViewerRef, useViewer } from './use-viewer.js';
|
||||
export { useDocument, useViewport, useActivePage } from './composables.js';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@zseven-w/op-web-sdk",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"description": "Read-only OpenPencil .op viewer SDK for the web (wasm-backed)",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Public entry for the OpenPencil read-only web SDK core.
|
||||
export const VERSION = '0.8.1';
|
||||
export const VERSION = '0.8.2';
|
||||
export { createViewer, OpViewer } from './viewer.js';
|
||||
export type { Viewport, CreateViewerOptions, PenDocument, PenPage } from './types.js';
|
||||
export type { ViewerEvent } from './events.js';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@zseven-w/openpencil-packages",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.2",
|
||||
"private": true,
|
||||
"description": "Web SDK workspace for OpenPencil (the Rust product lives in ../crates). Hosts the wasm-backed op-web-sdk viewer family plus the JS build glue that feeds the Rust web target.",
|
||||
"author": {
|
||||
|
|
|
|||
|
|
@ -278,6 +278,40 @@ export function inspectBunLockWorkspaceVersions(source) {
|
|||
return versions;
|
||||
}
|
||||
|
||||
export function renderBunLockWorkspaceVersions(source, version) {
|
||||
const rootEntries = readJsoncObjectEntries(source, 0);
|
||||
const workspaces = rootEntries.find((entry) => entry.key === 'workspaces');
|
||||
if (workspaces === undefined) {
|
||||
throw new Error('bun.lock does not contain a top-level workspaces object');
|
||||
}
|
||||
|
||||
const workspaceEntries = readJsoncObjectEntries(source, workspaces.valueStart);
|
||||
const replacements = sdkWorkspaceNames.map((workspaceName) => {
|
||||
const workspace = workspaceEntries.find((entry) => entry.key === workspaceName);
|
||||
if (workspace === undefined || source[workspace.valueStart] !== '{') {
|
||||
throw new Error(`bun.lock is missing workspace ${workspaceName}`);
|
||||
}
|
||||
const versionEntry = readJsoncObjectEntries(source, workspace.valueStart).find(
|
||||
(entry) => entry.key === 'version',
|
||||
);
|
||||
if (versionEntry === undefined || source[versionEntry.valueStart] !== '"') {
|
||||
throw new Error(`bun.lock workspace ${workspaceName} is missing a string version`);
|
||||
}
|
||||
return {
|
||||
start: versionEntry.valueStart,
|
||||
end: versionEntry.valueEnd,
|
||||
output: JSON.stringify(version),
|
||||
};
|
||||
});
|
||||
|
||||
let output = source;
|
||||
for (const replacement of replacements.toSorted((left, right) => right.start - left.start)) {
|
||||
output =
|
||||
output.slice(0, replacement.start) + replacement.output + output.slice(replacement.end);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
export function collectVersionDrift(expectedVersion, consumers) {
|
||||
return consumers
|
||||
.filter(({ actualVersion }) => actualVersion !== expectedVersion)
|
||||
|
|
@ -481,6 +515,13 @@ export async function synchronizeVersions({
|
|||
});
|
||||
}
|
||||
|
||||
const lockPath = resolve(packagesRoot, 'bun.lock');
|
||||
const lockSource = await readFile(lockPath, 'utf8');
|
||||
const lockOutput = renderBunLockWorkspaceVersions(lockSource, version);
|
||||
if (lockSource !== lockOutput) {
|
||||
await writeManagedFile(lockPath, lockOutput);
|
||||
}
|
||||
|
||||
const drift = collectVersionDrift(version, await readConsumers(packagesRoot));
|
||||
if (drift.length > 0) {
|
||||
throw postWriteValidationError(drift);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import * as syncVersion from './sync-version.mjs';
|
|||
import {
|
||||
collectVersionDrift,
|
||||
inspectBunLockWorkspaceVersions,
|
||||
renderBunLockWorkspaceVersions,
|
||||
renderPackageManifest,
|
||||
renderSdkEntry,
|
||||
} from './sync-version.mjs';
|
||||
|
|
@ -432,6 +433,25 @@ test('Bun lock inspection still rejects unterminated block comments after primit
|
|||
assert.throws(() => inspectBunLockWorkspaceVersions(lockfile), /unterminated block comment/i);
|
||||
});
|
||||
|
||||
test('Bun lock rendering updates only managed workspace version literals', () => {
|
||||
const lockfile = `{
|
||||
"workspaces": {
|
||||
"": { "name": "@zseven-w/openpencil-packages" },
|
||||
"op-web-sdk": { "version": "1.0.0", "fixture": "1.0.0" },
|
||||
"op-web-sdk-react": { "version": "1.0.1" },
|
||||
"op-web-sdk-vue": { "version": "1.0.2" },
|
||||
},
|
||||
}`;
|
||||
|
||||
const output = renderBunLockWorkspaceVersions(lockfile, '2.3.4');
|
||||
assert.deepEqual(inspectBunLockWorkspaceVersions(output), {
|
||||
'op-web-sdk': '2.3.4',
|
||||
'op-web-sdk-react': '2.3.4',
|
||||
'op-web-sdk-vue': '2.3.4',
|
||||
});
|
||||
assert.match(output, /"fixture": "1\.0\.0"/);
|
||||
});
|
||||
|
||||
test('drift collection reports each stale path with expected and actual versions', () => {
|
||||
assert.deepEqual(
|
||||
collectVersionDrift('2.3.4', [
|
||||
|
|
@ -608,22 +628,27 @@ test('write mode restores every managed file when Bun lock regeneration fails',
|
|||
assert.deepEqual(await readManagedFiles(fixture.managedPaths), before);
|
||||
});
|
||||
|
||||
test('write mode restores every managed file when post-write validation fails', async (t) => {
|
||||
test('write mode repairs workspace versions when Bun leaves them stale', async (t) => {
|
||||
const fixture = await createRepositoryFixture(t);
|
||||
const before = await readManagedFiles(fixture.managedPaths);
|
||||
const runner = createCommandRunner(fixture.packagesRoot, '2.3.4', {
|
||||
regenerateLock: false,
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
syncVersion.synchronizeVersions({
|
||||
mode: 'write',
|
||||
...fixture,
|
||||
runCommand: runner.runCommand,
|
||||
}),
|
||||
/post-write validation failed.*bun\.lock/i,
|
||||
const result = await syncVersion.synchronizeVersions({
|
||||
mode: 'write',
|
||||
...fixture,
|
||||
runCommand: runner.runCommand,
|
||||
});
|
||||
|
||||
assert.deepEqual(result.drift, []);
|
||||
assert.deepEqual(
|
||||
inspectBunLockWorkspaceVersions(await readFile(fixture.managedPaths[7], 'utf8')),
|
||||
{
|
||||
'op-web-sdk': '2.3.4',
|
||||
'op-web-sdk-react': '2.3.4',
|
||||
'op-web-sdk-vue': '2.3.4',
|
||||
},
|
||||
);
|
||||
assert.deepEqual(await readManagedFiles(fixture.managedPaths), before);
|
||||
});
|
||||
|
||||
test('write mode waits for sibling writes to settle before rollback', async (t) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue