From f1dc043593cd4c5aba7855e9e45ba8c60cef4af7 Mon Sep 17 00:00:00 2001 From: Ben Jacobson Date: Mon, 12 Oct 2026 20:37:55 +0000 Subject: [PATCH 1/2] Upgrade Effect and Foldkit with tested compatibility exceptions --- .changeset/proud-banks-doubt.md | 28 + README.md | 28 +- apps/demo/e2e/desktop.scenarios.ts | 230 +- apps/demo/e2e/editor.e2e.test.ts | 15 +- apps/demo/e2e/native-editor.scenarios.ts | 11 +- apps/demo/e2e/orchestrator.scenarios.ts | 1 + apps/demo/e2e/stateful-ui.scenarios.ts | 109 +- apps/demo/e2e/workflow.e2e.test.ts | 2 +- apps/demo/package.json | 1 + apps/demo/src/app/subscriptions.ts | 22 +- apps/demo/src/app/update.ts | 47 +- apps/demo/src/data-grid/subscriptions.ts | 5 +- apps/demo/src/data-grid/update.ts | 10 +- apps/demo/src/form-builder/field-types.ts | 126 +- apps/demo/src/form-builder/subscriptions.ts | 3 +- apps/demo/src/form-builder/update.ts | 665 ++++-- apps/demo/src/generative-ui/mcp-app.ts | 21 +- apps/demo/src/outliner/update.ts | 8 +- apps/demo/src/query-builder/subscriptions.ts | 3 +- apps/demo/src/query-builder/update.ts | 14 +- apps/demo/src/statechart/subscriptions.ts | 3 +- apps/demo/src/ui-kit/update.ts | 57 +- apps/demo/src/workbench/subscriptions.ts | 3 +- apps/demo/src/workflow/subscriptions.ts | 3 +- apps/demo/src/workflow/update.ts | 385 +-- apps/demo/vitest.e2e.config.ts | 2 + package.json | 12 +- packages/agent/src/update.ts | 374 +-- packages/code-editor/src/native/view.ts | 59 +- packages/editor/src/browser.ts | 4 +- packages/editor/src/node-view.ts | 4 +- packages/generative-ui/README.md | 2 +- packages/generative-ui/src/ai.ts | 2 +- .../generative-ui/src/generative-ui.test.ts | 2 +- packages/generative-ui/src/mcp.ts | 2 +- packages/outliner/src/view.ts | 60 +- packages/pdf-annotator/src/subscriptions.ts | 31 +- packages/pdf-annotator/src/view.ts | 16 +- packages/query-builder/src/subscriptions.ts | 3 +- packages/query-builder/src/update.ts | 4 +- packages/sidebar/src/update.ts | 12 +- packages/ui/README.md | 5 + packages/ui/src/animation.test.ts | 5 +- packages/ui/src/checkbox.ts | 1 + packages/ui/src/date-input.ts | 1 + packages/ui/src/editable-text.ts | 35 +- packages/ui/src/field.ts | 3 + packages/ui/src/fieldset.ts | 81 +- packages/ui/src/foldkit-attribute.test.ts | 2 +- packages/ui/src/stateful.test.ts | 168 +- packages/ui/src/switch.ts | 123 +- patches/foldkit@0.156.0.patch | 49 - patches/foldkit@0.168.0.patch | 30 + pnpm-lock.yaml | 2090 +++++------------ pnpm-workspace.yaml | 44 +- scripts/attribute-repro.mjs | 2 +- scripts/check-packages.mjs | 18 +- scripts/infra.test.ts | 13 + tsconfig.json | 2 +- vitest.infra.config.ts | 3 + 60 files changed, 2328 insertions(+), 2736 deletions(-) create mode 100644 .changeset/proud-banks-doubt.md delete mode 100644 patches/foldkit@0.156.0.patch create mode 100644 patches/foldkit@0.168.0.patch create mode 100644 scripts/infra.test.ts create mode 100644 vitest.infra.config.ts diff --git a/.changeset/proud-banks-doubt.md b/.changeset/proud-banks-doubt.md new file mode 100644 index 0000000..83b9788 --- /dev/null +++ b/.changeset/proud-banks-doubt.md @@ -0,0 +1,28 @@ +--- +"@foldworks/agent": minor +"@foldworks/code-editor": minor +"@foldworks/data-grid": minor +"@foldworks/data-table": minor +"@foldworks/diagram": minor +"@foldworks/diff-viewer": minor +"@foldworks/editor": minor +"@foldworks/form-builder": minor +"@foldworks/generative-ui": minor +"@foldworks/history": minor +"@foldworks/keyboard": minor +"@foldworks/outliner": minor +"@foldworks/pdf": minor +"@foldworks/pdf-annotator": minor +"@foldworks/pdf-viewer": minor +"@foldworks/query-builder": minor +"@foldworks/sidebar": minor +"@foldworks/text-intelligence": minor +"@foldworks/ui": minor +"@foldworks/workflow": minor +--- + +Require the tested Effect 4.0.3 and Foldkit 0.168.0 shared runtime peers. +Migrate renamed Foldkit APIs, textarea values, custom-event messages, and Effect +AI imports. Update the re-exported headless UI APIs to @foldkit/ui 0.168.0. +Preserve description linkage and supply the calendar format data required by +the new headless APIs. diff --git a/README.md b/README.md index 3855d12..34a8f7c 100644 --- a/README.md +++ b/README.md @@ -147,10 +147,30 @@ Published packages declare Effect, Foldkit, `@foldkit/ui`, and StyleX as peers when they use them. Applications can supply compatible versions of these shared runtimes; each package's peer ranges state what it supports. The named `peers` catalog holds those ranges, while the default catalog and lockfile keep exact -versions for builds and tests. Currently, -`foldkit@0.156.0` and `@foldkit/ui@0.156.0` themselves require Effect -`4.0.0-rc.112` exactly, so applications using those releases must use that -Effect version. Applications consuming StyleX components must also configure +versions for builds and tests. The tested combination is Effect `4.0.3`, +Foldkit and `@foldkit/ui` `0.168.0`, `@foldkit/markdown` `0.14.3`, and +`@foldkit/vite-plugin` `0.27.1`. + +Those Foldkit releases declare exact Effect `4.0.0` peers, including +`@effect/platform-browser` for Foldkit and DevTools. This repository allows +only `4.0.3` for those exact releases through scoped `peerDependencyRules` in +`pnpm-workspace.yaml`. The Vite plugin's two exact `4.0.0` Node platform +dependencies are overridden to `4.0.3` for that plugin release only. Alchemy's +SQL companions are pinned to `4.0.3` within its declared ranges. Alchemy and +its frontend adapter are upgraded together to `2.0.0-beta.80`, which uses +Effect's new import paths. An offline deployment-import test runs through +`@effect/vitest@4.0.3`; that adapter requires Vitest 5, so the test runner is +`5.0.3`. Other peer requirements remain checked. + +This is a local compatibility exception, not an upstream peer guarantee. +Applications installing these Foldkit releases with Effect `4.0.3` need the +same scoped peer rules (or npm overrides). The clean-consumer check in +`scripts/check-packages.mjs` demonstrates the npm configuration. Remove these +exceptions when upgrading to releases whose peers accept the tested Effect +version. The declaration-only Foldkit attribute patch is still carried for +TypeScript performance; see the [upstream log](./docs/foldkit-upstream.md). + +Applications consuming StyleX components must also configure the StyleX transform and its cascade layers; see the [`@foldworks/ui` setup](./packages/ui/README.md#setup). To test views without the StyleX compiler, use `foldworksStylexTest()` from `@foldworks/ui/vite`; see diff --git a/apps/demo/e2e/desktop.scenarios.ts b/apps/demo/e2e/desktop.scenarios.ts index fa83282..35a081e 100644 --- a/apps/demo/e2e/desktop.scenarios.ts +++ b/apps/demo/e2e/desktop.scenarios.ts @@ -12,23 +12,17 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { }; const focused = (page: Page, selector: string) => expect - .poll(() => - page.locator(selector).evaluate((el) => el === document.activeElement), - ) + .poll(() => page.locator(selector).evaluate((el) => el === document.activeElement)) .toBe(true); const announcement = (page: Page, text: string) => - expect - .poll(() => page.locator("#desktop-announcement").textContent()) - .toBe(text); + expect.poll(() => page.locator("#desktop-announcement").textContent()).toBe(text); describe("desktop primitives", () => { it("shares command execution across keyboard, menu, palette and help with scope and editable policies", async () => { const page = await visit(); const section = page.locator("#desktop-primitives"); await page.keyboard.press("Control+Shift+s"); await announcement(page, "Workspace saved (1)."); - await section - .getByRole("button", { name: "Workspace commands", exact: true }) - .click(); + await section.getByRole("button", { name: "Workspace commands", exact: true }).click(); await page.getByRole("menuitem", { name: /Save workspace/ }).click(); await announcement(page, "Workspace saved (2)."); const search = section.getByRole("combobox", { @@ -39,20 +33,14 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await announcement(page, "Workspace saved (2)."); await search.press("Enter"); await announcement(page, "Workspace saved (3)."); - await section - .getByText("Keyboard shortcut reference", { exact: true }) - .click(); + await section.getByText("Keyboard shortcut reference", { exact: true }).click(); await section .locator("dl") .getByRole("button", { name: "Save workspace", exact: true }) .click(); await announcement(page, "Workspace saved (4)."); - const save = section - .getByRole("button", { name: "Save workspace", exact: true }) - .first(); - expect(await save.getAttribute("aria-keyshortcuts")).toBe( - "Control+Shift+s", - ); + const save = section.getByRole("button", { name: "Save workspace", exact: true }).first(); + expect(await save.getAttribute("aria-keyshortcuts")).toBe("Control+Shift+s"); await page.locator("#desktop-permissions").focus(); await page.keyboard.press("Control+Enter"); await announcement(page, "Inspector preview requested."); @@ -74,12 +62,8 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { exact: true, }); await toggle.waitFor(); - expect(await toggle.evaluate((el) => el === document.activeElement)).toBe( - true, - ); - const save = section - .getByRole("button", { name: "Save workspace", exact: true }) - .first(); + expect(await toggle.evaluate((el) => el === document.activeElement)).toBe(true); + const save = section.getByRole("button", { name: "Save workspace", exact: true }).first(); await expect.poll(() => save.isDisabled()).toBe(true); const prevented = await toggle.evaluate((el) => { const event = new KeyboardEvent("keydown", { @@ -94,13 +78,9 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { }); expect(prevented).toBe(false); await announcement(page, "Workspace ready."); - await section - .getByRole("button", { name: "Workspace commands", exact: true }) - .click(); + await section.getByRole("button", { name: "Workspace commands", exact: true }).click(); expect( - await page - .getByRole("menuitem", { name: /Save workspace/ }) - .getAttribute("aria-disabled"), + await page.getByRole("menuitem", { name: /Save workspace/ }).getAttribute("aria-disabled"), ).toBe("true"); await page.keyboard.press("Escape"); await toggle.click(); @@ -154,9 +134,7 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await input.fill("Discard"); await input.press("Escape"); await expect.poll(() => trigger.textContent()).toBe("Release gates"); - await page - .getByRole("button", { name: "Edit description", exact: true }) - .click(); + await page.getByRole("button", { name: "Edit description", exact: true }).click(); const multiline = page.getByRole("textbox", { name: "description", exact: true, @@ -169,9 +147,7 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await multiline.press("Control+Enter"); await expect .poll(() => - page - .getByRole("button", { name: "Edit description", exact: true }) - .textContent(), + page.getByRole("button", { name: "Edit description", exact: true }).textContent(), ) .toBe("First\nx"); }); @@ -183,22 +159,19 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { }); await input.fill("Oper"); await page + .locator("#desktop-tags-suggestions-items") .getByRole("option", { name: "Operations", exact: true }) - .first() .click(); await expect - .poll(() => - page.locator('#desktop-tags [data-token="Operations"]').count(), - ) + .poll(() => page.locator('#desktop-tags [data-token="Operations"]').count()) .toBe(1); await input.fill("Research"); await page + .locator("#desktop-tags-suggestions-items") .getByRole("option", { name: "Add “Research”", exact: true }) .click(); await expect - .poll(() => - page.locator('#desktop-tags [data-token="Research"]').count(), - ) + .poll(() => page.locator('#desktop-tags [data-token="Research"]').count()) .toBe(1); await input.fill(""); const paste = (text: string) => @@ -214,74 +187,55 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { ); }, text); await paste(" Platform; Legal "); - await expect - .poll(() => page.locator('#desktop-tags [data-token="Legal"]').count()) - .toBe(1); + await expect.poll(() => page.locator('#desktop-tags [data-token="Legal"]').count()).toBe(1); await paste("Extra; Another"); await expect .poll(() => page.locator("#desktop-tags-status").textContent()) .toBe("Choose at most 5 values."); await input.focus(); await input.press("Backspace"); - await focused( - page, - '#desktop-tags [data-token="Legal"] button:first-child', - ); + await focused(page, '#desktop-tags [data-token="Legal"] button:first-child'); await page.keyboard.press("Delete"); - await expect - .poll(() => page.locator('#desktop-tags [data-token="Legal"]').count()) - .toBe(0); - await page - .getByRole("button", { name: "Remove Research", exact: true }) - .click(); + await expect.poll(() => page.locator('#desktop-tags [data-token="Legal"]').count()).toBe(0); + await page.getByRole("button", { name: "Remove Research", exact: true }).click(); await expect .poll(() => page .locator("#desktop-form") - .evaluate((form) => - new FormData(form as HTMLFormElement).getAll("teams"), - ), + .evaluate((form) => new FormData(form as HTMLFormElement).getAll("teams")), ) .toEqual(["Platform", "Design", "Operations"]); }); it("restores focus through a controlled nested inspector and respects reduced motion", async () => { const page = await visit(); await page.emulateMedia({ reducedMotion: "reduce" }); - await page - .getByRole("button", { name: "Permissions", exact: true }) - .click(); + await page.getByRole("button", { name: "Permissions", exact: true }).click(); await focused(page, "#desktop-stack-heading"); expect( - await page - .locator('[data-panel-id="permissions"]') - .evaluate((panel) => { - const input = document.createElement("input"); - panel.append(input); - const event = new KeyboardEvent("keydown", { - key: "ArrowLeft", - altKey: true, - bubbles: true, - cancelable: true, - }); - input.dispatchEvent(event); - input.remove(); - return event.defaultPrevented; - }), + await page.locator('[data-panel-id="permissions"]').evaluate((panel) => { + const input = document.createElement("input"); + panel.append(input); + const event = new KeyboardEvent("keydown", { + key: "ArrowLeft", + altKey: true, + bubbles: true, + cancelable: true, + }); + input.dispatchEvent(event); + input.remove(); + return event.defaultPrevented; + }), ).toBe(false); expect( await page .locator('[data-panel-id="permissions"]') .evaluate((el) => getComputedStyle(el).animationDuration), ).toBe("0s"); - await page - .getByRole("button", { name: "Advanced permissions", exact: true }) - .click(); + await page.getByRole("button", { name: "Advanced permissions", exact: true }).click(); await focused(page, "#desktop-stack-heading"); await page.keyboard.press("Alt+ArrowLeft"); await focused(page, "#desktop-advanced"); - await page - .getByRole("button", { name: "Back to previous panel", exact: true }) - .click(); + await page.getByRole("button", { name: "Back to previous panel", exact: true }).click(); await focused(page, "#desktop-permissions"); }); it("anchors context menus and navigates nested checkbox/radio items with keyboard and pointer intent", async () => { @@ -298,9 +252,7 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await page.keyboard.press("Enter"); await expect .poll(() => - page - .getByRole("menuitemcheckbox", { name: "Compact rows" }) - .getAttribute("aria-checked"), + page.getByRole("menuitemcheckbox", { name: "Compact rows" }).getAttribute("aria-checked"), ) .toBe("false"); await page.keyboard.press("ArrowDown"); @@ -309,9 +261,7 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await page.keyboard.press("Enter"); await expect .poll(() => - page - .getByRole("menuitemradio", { name: "Modified date" }) - .getAttribute("aria-checked"), + page.getByRole("menuitemradio", { name: "Modified date" }).getAttribute("aria-checked"), ) .toBe("true"); await page.keyboard.press("Escape"); @@ -323,12 +273,8 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await focused(page, "#desktop-context-trigger"); await trigger.press("Shift+F10"); await focused(page, "#desktop-context-layer"); - await page - .getByRole("menuitem", { name: "View options", exact: true }) - .hover(); - await page - .getByRole("menuitemcheckbox", { name: "Compact rows" }) - .waitFor(); + await page.getByRole("menuitem", { name: "View options", exact: true }).hover(); + await page.getByRole("menuitemcheckbox", { name: "Compact rows" }).waitFor(); await page.locator("#desktop-announcement").click(); await expect.poll(() => layer.count()).toBe(0); }); @@ -345,13 +291,9 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { .toBe("This date is unavailable."); await input.fill("28/09/2026"); await input.press("Enter"); - await page - .getByRole("button", { name: "Choose Deadline", exact: true }) - .click(); + await page.getByRole("button", { name: "Choose Deadline", exact: true }).click(); await expect - .poll(() => - page.evaluate(() => document.activeElement?.getAttribute("role")), - ) + .poll(() => page.evaluate(() => document.activeElement?.getAttribute("role"))) .toBe("grid"); expect( await page @@ -362,9 +304,7 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await page.keyboard.press("ArrowRight"); await expect .poll(() => - page.evaluate(() => - document.activeElement?.getAttribute("aria-activedescendant"), - ), + page.evaluate(() => document.activeElement?.getAttribute("aria-activedescendant")), ) .toContain("2026-9-29"); await page.keyboard.press("Enter"); @@ -372,16 +312,10 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { expect( await page .locator("#desktop-form") - .evaluate((form) => - new FormData(form as HTMLFormElement).get("deadline"), - ), + .evaluate((form) => new FormData(form as HTMLFormElement).get("deadline")), ).toBe("2026-09-29"); - await page - .getByRole("button", { name: "Choose Deadline", exact: true }) - .click(); - await page - .getByRole("button", { name: "Switch to month picker" }) - .click(); + await page.getByRole("button", { name: "Choose Deadline", exact: true }).click(); + await page.getByRole("button", { name: "Switch to month picker" }).click(); await expect .poll(() => page @@ -392,20 +326,12 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { .toBe(4); await page.getByRole("button", { name: "Switch to year picker" }).click(); await expect - .poll(() => - page.evaluate(() => - document.activeElement?.getAttribute("aria-label"), - ), - ) + .poll(() => page.evaluate(() => document.activeElement?.getAttribute("aria-label"))) .toContain("Year picker"); await page.keyboard.press("Enter"); await page.getByRole("grid", { name: /Month picker/ }).waitFor(); - await page - .getByRole("button", { name: "October 2026", exact: true }) - .click(); - await page - .getByRole("grid", { name: /Calendar, October 2026/ }) - .waitFor(); + await page.getByRole("button", { name: "October 2026", exact: true }).click(); + await page.getByRole("grid", { name: /Calendar, October 2026/ }).waitFor(); await page.keyboard.press("Escape"); expect(await input.inputValue()).toBe("29/09/2026"); }); @@ -438,18 +364,12 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { await expect .poll(() => page.evaluate( - () => - (window as unknown as { measuredObservers: number }) - .measuredObservers, + () => (window as unknown as { measuredObservers: number }).measuredObservers, ), ) .toBe(2); - await page - .getByRole("button", { name: "Open activity drawer", exact: true }) - .click(); - await page - .getByRole("button", { name: "Clear activity", exact: true }) - .click(); + await page.getByRole("button", { name: "Open activity drawer", exact: true }).click(); + await page.getByRole("button", { name: "Clear activity", exact: true }).click(); await expect .poll(() => page @@ -480,46 +400,30 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { .evaluate((el) => el === document.activeElement), ) .toBe(true); - await page - .getByRole("button", { name: "Open settings sheet", exact: true }) - .click(); - await page - .getByRole("button", { name: "Sheet view options", exact: true }) - .click(); + await page.getByRole("button", { name: "Open settings sheet", exact: true }).click(); + await page.getByRole("button", { name: "Sheet view options", exact: true }).click(); await focused(page, "#desktop-sheet-menu-layer"); await page.keyboard.press("ArrowDown"); await page.keyboard.press("ArrowRight"); - await page - .getByRole("menuitemcheckbox", { name: "Compact rows" }) - .waitFor(); + await page.getByRole("menuitemcheckbox", { name: "Compact rows" }).waitFor(); await page.keyboard.press("Escape"); expect(await page.locator("#desktop-sheet-menu-layer").count()).toBe(1); await page.keyboard.press("Escape"); - await expect - .poll(() => page.locator("#desktop-sheet-menu-layer").count()) - .toBe(0); + await expect.poll(() => page.locator("#desktop-sheet-menu-layer").count()).toBe(0); expect( - await page - .getByRole("dialog", { name: "Workspace settings", exact: true }) - .isVisible(), + await page.getByRole("dialog", { name: "Workspace settings", exact: true }).isVisible(), ).toBe(true); // Route removal while the sheet is still open exercises Dialog's unmount cleanup. await page .locator('a[href="/"]') .first() .evaluate((el) => (el as HTMLElement).click()); - await expect - .poll(() => page.locator("#desktop-primitives").count()) - .toBe(0); - await expect - .poll(() => page.evaluate(() => document.body.style.overflow)) - .not.toBe("hidden"); + await expect.poll(() => page.locator("#desktop-primitives").count()).toBe(0); + await expect.poll(() => page.evaluate(() => document.body.style.overflow)).not.toBe("hidden"); await expect .poll(() => page.evaluate( - () => - (window as unknown as { measuredObservers: number }) - .measuredObservers, + () => (window as unknown as { measuredObservers: number }).measuredObservers, ), ) .toBe(0); @@ -545,18 +449,12 @@ export const desktopScenarios = (getPage: () => Page, appUrl: string) => { }); await expect.poll(() => actions.getByRole("button").count()).toBe(5); await page.setViewportSize({ width: 390, height: 1600 }); - await expect - .poll(() => actions.getByText("More", { exact: true }).count()) - .toBe(1); + await expect.poll(() => actions.getByText("More", { exact: true }).count()).toBe(1); await actions.getByText("More", { exact: true }).click(); - await actions - .getByRole("button", { name: "Archive", exact: true }) - .click(); + await actions.getByRole("button", { name: "Archive", exact: true }).click(); await announcement(page, "Archive requested."); expect( - await page.evaluate( - () => document.documentElement.scrollWidth <= innerWidth + 1, - ), + await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1), ).toBe(true); await actions.getByText("More", { exact: true }).click(); await page diff --git a/apps/demo/e2e/editor.e2e.test.ts b/apps/demo/e2e/editor.e2e.test.ts index 283088e..f113652 100644 --- a/apps/demo/e2e/editor.e2e.test.ts +++ b/apps/demo/e2e/editor.e2e.test.ts @@ -50,7 +50,7 @@ const exported = async () => { return page.getByRole("textbox", { name: "Exported Markdown" }).inputValue(); }; -describe.sequential("native document editor", () => { +describe("native document editor", { concurrent: false }, () => { beforeAll(async () => { await build({ root: appRoot, @@ -96,6 +96,19 @@ describe.sequential("native document editor", () => { await server?.close(); }); + it("ignores malformed custom-event messages and keeps native edits working", async () => { + await source("Before"); + await root().evaluate((element) => { + for (const detail of [null, { _tag: "Input", text: 123 }]) { + element.dispatchEvent(new CustomEvent("foldworks-editor-message", { detail })); + } + }); + expect(await exported()).toBe("Before\n"); + await select(0, 6); + await page.keyboard.type(" after"); + expect(await exported()).toBe("Before after\n"); + }); + it("shares mention and tag completion, retains keyboard focus, and accepts in the middle", async () => { await source("Work @mystery, next"); await select(0, 7); diff --git a/apps/demo/e2e/native-editor.scenarios.ts b/apps/demo/e2e/native-editor.scenarios.ts index 2867d34..0471da5 100644 --- a/apps/demo/e2e/native-editor.scenarios.ts +++ b/apps/demo/e2e/native-editor.scenarios.ts @@ -158,6 +158,9 @@ export const nativeEditorScenarios = ( await expect .poll(() => editor(page).getByRole("option").allTextContents()) .toEqual(["LOG_LEVELdebug, info, warn, or error"]); + await expect + .poll(async () => (await snapshot(page)).completion?.items[0]?.insert) + .toBe("LOG_LEVEL="); await input(page).press("Enter"); await expect .poll(() => editor(page).getByRole("option").allTextContents()) @@ -176,8 +179,14 @@ export const nativeEditorScenarios = ( await expect .poll(() => input(page).inputValue()) .toBe(`${text}LOG_LEVEL=warn\nCACHE_URL=redis://\${REGION}\n`); + await expect + .poll(async () => + (await editor(page).getByRole("option").allTextContents()).some((option) => + option.startsWith("FEATURE_EXPORT"), + ), + ) + .toBe(true); const offered = await editor(page).getByRole("option").allTextContents(); - expect(offered.some((option) => option.startsWith("FEATURE_EXPORT"))).toBe(true); expect(offered.some((option) => option.startsWith("APP_ENV"))).toBe(false); await screenshot("native-editor-language-service"); }); diff --git a/apps/demo/e2e/orchestrator.scenarios.ts b/apps/demo/e2e/orchestrator.scenarios.ts index 60a9aa9..f79f930 100644 --- a/apps/demo/e2e/orchestrator.scenarios.ts +++ b/apps/demo/e2e/orchestrator.scenarios.ts @@ -13,6 +13,7 @@ export const orchestratorScenarios = ( await page.goto(`${appUrl}/orchestrator`, { waitUntil: "networkidle" }); const status = (id: string) => page.locator(`[data-thread-id="${id}"]`); await page.getByRole("button", { name: "Start task", exact: true }).first().click(); + await poll(() => page.getByRole("button", { name: "Start task", exact: true }).count()).toBe(1); await page.getByRole("button", { name: "Start task", exact: true }).click(); await page.getByRole("button", { name: "Collapse all branches", exact: true }).click(); await poll(() => page.locator('[data-outline-row="launch"]').innerText()).toContain( diff --git a/apps/demo/e2e/stateful-ui.scenarios.ts b/apps/demo/e2e/stateful-ui.scenarios.ts index f1671e5..90fd0c9 100644 --- a/apps/demo/e2e/stateful-ui.scenarios.ts +++ b/apps/demo/e2e/stateful-ui.scenarios.ts @@ -1,7 +1,11 @@ import type { Page } from "playwright"; import { describe, expect, it } from "vitest"; -export const statefulUiScenarios = (getPage: () => Page, appUrl: string, screenshot: (name: string) => Promise) => { +export const statefulUiScenarios = ( + getPage: () => Page, + appUrl: string, + screenshot: (name: string) => Promise, +) => { describe("stateful UI", () => { const start = async () => { const page = getPage(); @@ -14,16 +18,22 @@ export const statefulUiScenarios = (getPage: () => Page, appUrl: string, screens const tabs = page.getByRole("tablist", { name: "Account views" }); await tabs.getByRole("tab", { name: "Overview" }).focus(); await page.keyboard.press("ArrowRight"); - await expect.poll(() => tabs.getByRole("tab", { name: "Details" }).getAttribute("aria-selected")).toBe("true"); + await expect + .poll(() => tabs.getByRole("tab", { name: "Details" }).getAttribute("aria-selected")) + .toBe("true"); expect(await page.locator(":focus").textContent()).toBe("Details"); - expect(await page.getByRole("tabpanel", { name: "Details" }).textContent()).toBe("Account details"); + expect(await page.getByRole("tabpanel", { name: "Details" }).textContent()).toBe( + "Account details", + ); await page.keyboard.press("End"); await expect.poll(() => page.locator(":focus").textContent()).toBe("Activity"); await page.keyboard.press("ArrowRight"); await expect.poll(() => page.locator(":focus").textContent()).toBe("Overview"); await page.keyboard.press("End"); await page.keyboard.press("Home"); - await expect.poll(() => tabs.getByRole("tab", { name: "Overview" }).getAttribute("aria-selected")).toBe("true"); + await expect + .poll(() => tabs.getByRole("tab", { name: "Overview" }).getAttribute("aria-selected")) + .toBe("true"); }); it("searches and selects commands entirely from the keyboard", async () => { @@ -31,15 +41,23 @@ export const statefulUiScenarios = (getPage: () => Page, appUrl: string, screens const input = page.getByRole("combobox", { name: "Workspace commands", exact: true }); await input.focus(); await page.keyboard.press("ArrowDown"); - await expect.poll(() => input.getAttribute("aria-activedescendant")).toBe("catalog-command-item-settings"); + await expect + .poll(() => input.getAttribute("aria-activedescendant")) + .toBe("catalog-command-item-settings"); expect(await input.evaluate((element) => element === document.activeElement)).toBe(true); await input.fill("prfrncs"); - await expect.poll(() => page.getByRole("option", { name: "Open settings" }).isVisible()).toBe(true); + await expect + .poll(() => page.getByRole("option", { name: "Open settings" }).isVisible()) + .toBe(true); await page.keyboard.press("Enter"); - await expect.poll(() => page.locator('[data-ui-kit] [aria-live="polite"]').first().textContent()).toBe("settings command selected."); + await expect + .poll(() => page.locator('[data-ui-kit] [aria-live="polite"]').first().textContent()) + .toBe("settings command selected."); expect(await input.inputValue()).toBe("prfrncs"); await input.fill("no matching command"); - await expect.poll(() => page.getByRole("status").allTextContents()).toContain("No results found."); + await expect + .poll(() => page.getByRole("status").allTextContents()) + .toContain("No results found."); expect(await input.getAttribute("aria-activedescendant")).toBeNull(); }); @@ -61,24 +79,38 @@ export const statefulUiScenarios = (getPage: () => Page, appUrl: string, screens await page.keyboard.press("ArrowDown"); const listbox = page.getByRole("listbox", { name: "Custom department select" }); await expect.poll(() => listbox.isVisible()).toBe(true); - await expect.poll(() => listbox.evaluate((element) => element === document.activeElement)).toBe(true); + await expect + .poll(() => listbox.evaluate((element) => element === document.activeElement)) + .toBe(true); await page.keyboard.press("Home"); - await expect.poll(() => listbox.getAttribute("aria-activedescendant")).toBe("catalog-select-item-0"); + await expect + .poll(() => listbox.getAttribute("aria-activedescendant")) + .toBe("catalog-select-item-0"); await page.keyboard.press("ArrowDown"); - await expect.poll(() => listbox.getAttribute("aria-activedescendant")).toBe("catalog-select-item-2"); - expect(await listbox.getByRole("option", { name: "Operations" }).getAttribute("aria-disabled")).toBe("true"); + await expect + .poll(() => listbox.getAttribute("aria-activedescendant")) + .toBe("catalog-select-item-2"); + expect( + await listbox.getByRole("option", { name: "Operations" }).getAttribute("aria-disabled"), + ).toBe("true"); await page.keyboard.type("eng"); - await expect.poll(() => listbox.getAttribute("aria-activedescendant")).toBe("catalog-select-item-0"); + await expect + .poll(() => listbox.getAttribute("aria-activedescendant")) + .toBe("catalog-select-item-0"); await page.keyboard.press("Enter"); await expect.poll(() => listbox.isVisible()).toBe(false); await expect.poll(() => trigger.textContent()).toContain("Engineering"); - await expect.poll(() => trigger.evaluate((element) => element === document.activeElement)).toBe(true); + await expect + .poll(() => trigger.evaluate((element) => element === document.activeElement)) + .toBe(true); expect(await page.locator('input[name="department"]').inputValue()).toBe("Engineering"); await trigger.click(); await expect.poll(() => listbox.isVisible()).toBe(true); await page.keyboard.press("Escape"); await expect.poll(() => listbox.isVisible()).toBe(false); - await expect.poll(() => trigger.evaluate((element) => element === document.activeElement)).toBe(true); + await expect + .poll(() => trigger.evaluate((element) => element === document.activeElement)) + .toBe(true); }); it("traps dialog focus, restores the trigger, and honors custom surface colors", async () => { @@ -93,22 +125,38 @@ export const statefulUiScenarios = (getPage: () => Page, appUrl: string, screens await trigger.click(); const dialog = page.getByRole("dialog", { name: "Dialog example", exact: true }); const input = dialog.getByRole("textbox", { name: "Display name" }); - await expect.poll(() => input.evaluate((element) => element === document.activeElement)).toBe(true); + await expect + .poll(() => input.evaluate((element) => element === document.activeElement)) + .toBe(true); expect(await page.evaluate(() => document.documentElement.style.overflow)).toBe("hidden"); - expect(await page.locator("#catalog-dialog-panel").evaluate((element) => ({ - background: getComputedStyle(element).backgroundColor, - foreground: getComputedStyle(element).color, - }))).toEqual({ background: "rgb(20, 30, 40)", foreground: "rgb(230, 240, 250)" }); - expect(await input.evaluate((element) => getComputedStyle(element).color)).toBe("rgb(230, 240, 250)"); + expect( + await page.locator("#catalog-dialog-panel").evaluate((element) => ({ + background: getComputedStyle(element).backgroundColor, + foreground: getComputedStyle(element).color, + })), + ).toEqual({ background: "rgb(20, 30, 40)", foreground: "rgb(230, 240, 250)" }); + expect(await input.evaluate((element) => getComputedStyle(element).color)).toBe( + "rgb(230, 240, 250)", + ); await page.keyboard.press("Shift+Tab"); - await expect.poll(() => dialog.getByRole("button", { name: "Close" }).evaluate((element) => element === document.activeElement)).toBe(true); + await expect + .poll(() => + dialog + .getByRole("button", { name: "Close" }) + .evaluate((element) => element === document.activeElement), + ) + .toBe(true); await page.keyboard.press("Tab"); - await expect.poll(() => input.evaluate((element) => element === document.activeElement)).toBe(true); + await expect + .poll(() => input.evaluate((element) => element === document.activeElement)) + .toBe(true); await input.fill("Updated profile"); await screenshot("stateful-dialog"); await page.keyboard.press("Escape"); await expect.poll(() => dialog.isVisible()).toBe(false); - await expect.poll(() => trigger.evaluate((element) => element === document.activeElement)).toBe(true); + await expect + .poll(() => trigger.evaluate((element) => element === document.activeElement)) + .toBe(true); expect(await page.evaluate(() => document.documentElement.style.overflow)).not.toBe("hidden"); await trigger.click(); await expect.poll(() => input.inputValue()).toBe("Updated profile"); @@ -127,11 +175,18 @@ export const statefulUiScenarios = (getPage: () => Page, appUrl: string, screens await dialog.getByRole("button", { name: "Close" }).click(); await expect.poll(() => dialog.isVisible()).toBe(false); await trigger.click(); - await expect.poll(() => page.evaluate(() => document.documentElement.style.overflow)).toBe("hidden"); + await expect + .poll(() => page.evaluate(() => document.documentElement.style.overflow)) + .toBe("hidden"); // Drive the application's client router while the dialog subtree is mounted. - await page.getByRole("link", { name: "Data grid", exact: true }).evaluate((element) => (element as HTMLAnchorElement).click()); + await page + .locator("#foldworks-sidebar-desktop") + .getByRole("link", { name: "Data grid", exact: true, includeHidden: true }) + .evaluate((element) => (element as HTMLAnchorElement).click()); await expect.poll(() => page.url()).toContain("/data-grid"); - await expect.poll(() => page.evaluate(() => document.documentElement.style.overflow)).not.toBe("hidden"); + await expect + .poll(() => page.evaluate(() => document.documentElement.style.overflow)) + .not.toBe("hidden"); }); }); }; diff --git a/apps/demo/e2e/workflow.e2e.test.ts b/apps/demo/e2e/workflow.e2e.test.ts index f12ba13..b086752 100644 --- a/apps/demo/e2e/workflow.e2e.test.ts +++ b/apps/demo/e2e/workflow.e2e.test.ts @@ -96,7 +96,7 @@ const centerQueryGroup = (groupId: string) => .locator(`[data-query-group="${groupId}"]`) .evaluate((element) => element.scrollIntoView({ block: "center" })); -describe.sequential("structured workflow builder", () => { +describe("structured workflow builder", { concurrent: false }, () => { beforeAll(async () => { await mkdir(screenshotDirectory, { recursive: true }); await build({ diff --git a/apps/demo/package.json b/apps/demo/package.json index 87bddd9..1943368 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -42,6 +42,7 @@ "pdf-lib": "catalog:" }, "devDependencies": { + "@effect/platform-browser": "catalog:", "@foldkit/devtools": "catalog:", "@foldkit/vite-plugin": "catalog:", "@playwright/test": "catalog:", diff --git a/apps/demo/src/app/subscriptions.ts b/apps/demo/src/app/subscriptions.ts index 6860f6c..8a15666 100644 --- a/apps/demo/src/app/subscriptions.ts +++ b/apps/demo/src/app/subscriptions.ts @@ -1,5 +1,5 @@ import { subscriptions as orchestratorSubscriptions } from "../orchestrator/subscriptions"; -import { Effect, Schema as S, Stream } from "effect"; +import { Effect, Option, Schema as S, Stream } from "effect"; import { PdfAnnotator } from "@foldworks/pdf-annotator"; import { Subscription } from "foldkit"; @@ -73,7 +73,7 @@ const historySubscriptions = Subscription.make()((entry) => ({ })); const themeSubscriptions = Subscription.make()(() => ({ - systemTheme: Subscription.persistent( + systemTheme: Subscription.persistentEntry( Stream.fromEventListener( window.matchMedia(SYSTEM_DARK_QUERY), "change", @@ -82,50 +82,50 @@ const themeSubscriptions = Subscription.make()(() => ({ })); const workflow = Subscription.lift(workflowSubscriptions)({ - toChildModel: (model) => model.workflowEditor, + read: (model) => Option.some(model.workflowEditor), toParentMessage: (message) => Message.GotWorkflowEditorMessage({ message }), }); const statechart = Subscription.lift(statechartSubscriptions)({ when: (model) => model.route._tag === "Statechart", - toChildModel: (model) => model.statechart, + read: (model) => Option.some(model.statechart), toParentMessage: (message) => Message.GotStatechartMessage({ message }), }); const orchestrator = Subscription.lift(orchestratorSubscriptions)({ - toChildModel: (model) => model.orchestrator, + read: (model) => Option.some(model.orchestrator), toParentMessage: (message) => Message.GotOrchestratorMessage({ message }), }); const agent = Subscription.lift(agentSubscriptions)({ when: (model) => model.route._tag === "Agent", - toChildModel: (model) => model.agent, + read: (model) => Option.some(model.agent), toParentMessage: (message) => Message.GotAgentMessage({ message }), }); const form = Subscription.lift(formSubscriptions)({ - toChildModel: (model) => model.formEditor, + read: (model) => Option.some(model.formEditor), toParentMessage: (message) => Message.GotFormEditorMessage({ message }), }); const workbench = Subscription.lift(workbenchSubscriptions)({ when: (model) => model.route._tag === "Workbench", - toChildModel: (model) => model.workbench, + read: (model) => Option.some(model.workbench), toParentMessage: (message) => Message.GotWorkbenchMessage({ message }), }); const dataGrid = Subscription.lift(dataGridSubscriptions)({ - toChildModel: (model) => model.dataGridDemo, + read: (model) => Option.some(model.dataGridDemo), toParentMessage: (message) => Message.GotDataGridDemoMessage({ message }), }); const queryBuilder = Subscription.lift(queryBuilderSubscriptions)({ - toChildModel: (model) => model.queryBuilderDemo, + read: (model) => Option.some(model.queryBuilderDemo), toParentMessage: (message) => Message.GotQueryBuilderDemoMessage({ message }), }); const pdfAnnotator = Subscription.lift(PdfAnnotator.subscriptions)({ - toChildModel: (model) => model.pdfAnnotator, + read: (model) => Option.some(model.pdfAnnotator), toParentMessage: (message) => Message.GotPdfAnnotatorMessage({ message }), }); diff --git a/apps/demo/src/app/update.ts b/apps/demo/src/app/update.ts index b82c9d5..cad14b6 100644 --- a/apps/demo/src/app/update.ts +++ b/apps/demo/src/app/update.ts @@ -8,7 +8,7 @@ import { Sidebar } from "@foldworks/sidebar"; import { Command, Update } from "foldkit"; import { scrollIntoViewAfterPaint } from "foldkit/dom"; import { UrlRequest, load, pushUrl } from "foldkit/navigation"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { toString as urlToString } from "foldkit/url"; import { update as updateCodeEditor } from "../code-editor/update"; @@ -92,7 +92,7 @@ const PersistWorkspace = Command.define("PersistWorkspace", { }); const persist = (model: Model, announcement: string): UpdateReturn => { - const next = evo(model, { + const next = modifyFields(model, { persistenceStatus: () => "Saving" as const, revision: (value) => value + 1, announcement: () => announcement, @@ -114,7 +114,7 @@ const persist = (model: Model, announcement: string): UpdateReturn => { const foldWorkflow = Update.foldChild({ update: updateWorkflow, read: (model: Model) => Option.some(model.workflowEditor), - write: (model, workflowEditor) => evo(model, { workflowEditor: () => workflowEditor }), + write: (model, workflowEditor) => modifyFields(model, { workflowEditor: () => workflowEditor }), toParentMessage: (message) => Message.GotWorkflowEditorMessage({ message }), foldOutMessage: (outMessage: WorkflowOutMessage) => @@ -131,7 +131,7 @@ const foldWorkflow = Update.foldChild({ const foldForm = Update.foldChild({ update: updateForm, read: (model: Model) => Option.some(model.formEditor), - write: (model, formEditor) => evo(model, { formEditor: () => formEditor }), + write: (model, formEditor) => modifyFields(model, { formEditor: () => formEditor }), toParentMessage: (message) => Message.GotFormEditorMessage({ message }), foldOutMessage: (outMessage: FormOutMessage) => @@ -148,7 +148,7 @@ const foldForm = Update.foldChild({ const foldStatechart = Update.foldChild({ update: updateStatechart, read: (model: Model) => Option.some(model.statechart), - write: (model, statechart) => evo(model, { statechart: () => statechart }), + write: (model, statechart) => modifyFields(model, { statechart: () => statechart }), toParentMessage: (message) => Message.GotStatechartMessage({ message }), }); @@ -207,7 +207,7 @@ const foldWorkbench = Update.foldChild({ const foldDataGrid = Update.foldChild({ update: updateDataGrid, read: (model: Model) => Option.some(model.dataGridDemo), - write: (model, dataGridDemo) => evo(model, { dataGridDemo: () => dataGridDemo }), + write: (model, dataGridDemo) => modifyFields(model, { dataGridDemo: () => dataGridDemo }), toParentMessage: (message) => Message.GotDataGridDemoMessage({ message }), }); @@ -228,35 +228,36 @@ const foldDiffViewer = Update.foldChild({ const foldUiKit = Update.foldChild({ update: updateUiKit, read: (model: Model) => Option.some(model.uiKit), - write: (model, uiKit) => evo(model, { uiKit: () => uiKit }), + write: (model, uiKit) => modifyFields(model, { uiKit: () => uiKit }), toParentMessage: (message) => Message.GotUiKitMessage({ message }), }); const foldQueryBuilder = Update.foldChild({ update: updateQueryBuilder, read: (model: Model) => Option.some(model.queryBuilderDemo), - write: (model, queryBuilderDemo) => evo(model, { queryBuilderDemo: () => queryBuilderDemo }), + write: (model, queryBuilderDemo) => + modifyFields(model, { queryBuilderDemo: () => queryBuilderDemo }), toParentMessage: (message) => Message.GotQueryBuilderDemoMessage({ message }), }); const foldOutliner = Update.foldChild({ update: updateOutliner, read: (model: Model) => Option.some(model.outlinerDemo), - write: (model, outlinerDemo) => evo(model, { outlinerDemo: () => outlinerDemo }), + write: (model, outlinerDemo) => modifyFields(model, { outlinerDemo: () => outlinerDemo }), toParentMessage: (message) => Message.GotOutlinerDemoMessage({ message }), }); const foldPdfAnnotator = Update.foldChild({ update: PdfAnnotator.update, read: (model: Model) => Option.some(model.pdfAnnotator), - write: (model, pdfAnnotator) => evo(model, { pdfAnnotator: () => pdfAnnotator }), + write: (model, pdfAnnotator) => modifyFields(model, { pdfAnnotator: () => pdfAnnotator }), toParentMessage: (message) => Message.GotPdfAnnotatorMessage({ message }), }); const foldPdfViewer = Update.foldChild({ update: updatePdfViewer, read: (model: Model) => Option.some(model.pdfViewerDemo), - write: (model, pdfViewerDemo) => evo(model, { pdfViewerDemo: () => pdfViewerDemo }), + write: (model, pdfViewerDemo) => modifyFields(model, { pdfViewerDemo: () => pdfViewerDemo }), toParentMessage: (message) => Message.GotPdfViewerDemoMessage({ message }), }); @@ -279,12 +280,12 @@ const foldEditor = Update.foldChild({ const foldSidebar = Update.foldChild({ update: Sidebar.update, read: (model: Model) => Option.some(model.sidebar), - write: (model, sidebar) => evo(model, { sidebar: () => sidebar }), + write: (model, sidebar) => modifyFields(model, { sidebar: () => sidebar }), toParentMessage: (message) => Message.GotSidebarMessage({ message }), }); const applyRoute = (model: Model, route: Model["route"]): Model => { - let next: Model = evo(model, { route: () => route }); + let next: Model = modifyFields(model, { route: () => route }); if ( route._tag === "Docs" && (model.route._tag !== "Docs" || @@ -311,7 +312,7 @@ const applyRoute = (model: Model, route: Model["route"]): Model => { const workflowEditor = setOrientation(next.workflowEditor, workflowOrientationFromRoute(route)); return workflowEditor === next.workflowEditor ? next - : evo(next, { + : modifyFields(next, { workflowEditor: () => workflowEditor, revision: (value) => value + 1, }); @@ -321,12 +322,14 @@ const applyRoute = (model: Model, route: Model["route"]): Model => { const { exampleId, mode } = formStateFromRoute(route); const previousEditor = next.formEditor; if (previousEditor.exampleId !== exampleId) { - next = evo(next, { formEditor: (editor) => loadExample(editor, exampleId) }); + next = modifyFields(next, { formEditor: (editor) => loadExample(editor, exampleId) }); } if (next.formEditor.mode !== mode) { - next = evo(next, { formEditor: (editor) => setMode(editor, mode) }); + next = modifyFields(next, { formEditor: (editor) => setMode(editor, mode) }); } - return next.formEditor === previousEditor ? next : evo(next, { revision: (value) => value + 1 }); + return next.formEditor === previousEditor + ? next + : modifyFields(next, { revision: (value) => value + 1 }); }; export const update = (model: Model, message: Message): UpdateReturn => @@ -335,14 +338,14 @@ export const update = (model: Model, message: Message): UpdateReturn => CompletedLoadExternal: () => ({ model }), CompletedApplyTheme: () => ({ model }), CompletedPersistWorkspace: ({ succeeded }) => ({ - model: evo(model, { + model: modifyFields(model, { persistenceStatus: () => (succeeded ? "Saved" : "Error"), announcement: () => succeeded ? "Changes saved locally." : "Changes could not be saved in this browser.", }), }), ChangedSystemTheme: ({ isDark }) => { - const next = evo(model, { systemIsDark: () => isDark }); + const next = modifyFields(model, { systemIsDark: () => isDark }); return model.themePreference === "System" ? { model: next, @@ -357,7 +360,7 @@ export const update = (model: Model, message: Message): UpdateReturn => : { model: next }; }, SelectedThemeName: ({ name }) => ({ - model: evo(model, { themeName: () => name }), + model: modifyFields(model, { themeName: () => name }), commands: [ ApplyTheme({ name, @@ -367,7 +370,7 @@ export const update = (model: Model, message: Message): UpdateReturn => ], }), SelectedThemePreference: ({ preference }) => ({ - model: evo(model, { themePreference: () => preference }), + model: modifyFields(model, { themePreference: () => preference }), commands: [ ApplyTheme({ name: model.themeName, @@ -417,7 +420,7 @@ export const update = (model: Model, message: Message): UpdateReturn => GotThemeBuilderMessage: ({ message }) => foldThemeBuilder(model, message), GotUiKitMessage: ({ message: childMessage }) => foldUiKit(model, childMessage), GotGenerativeUiAction: ({ intent }) => ({ - model: evo(model, { + model: modifyFields(model, { announcement: () => `${intent.action.replaceAll("_", " ")} requested from ${intent.elementId}.`, }), diff --git a/apps/demo/src/data-grid/subscriptions.ts b/apps/demo/src/data-grid/subscriptions.ts index e1c2f70..ae999b8 100644 --- a/apps/demo/src/data-grid/subscriptions.ts +++ b/apps/demo/src/data-grid/subscriptions.ts @@ -1,3 +1,4 @@ +import { Option } from "effect"; import { Subscription } from "foldkit"; import { DataGrid } from "@foldworks/data-grid"; @@ -6,12 +7,12 @@ import { Message } from "./message"; import type { Model } from "./model"; const worksheet = Subscription.lift(DataGrid.subscriptions)({ - toChildModel: (model) => model.grid, + read: (model) => Option.some(model.grid), toParentMessage: (message) => Message.GotGridMessage({ message }), }); const coverage = Subscription.lift(DataGrid.subscriptions)({ - toChildModel: (model) => model.coverage.grid, + read: (model) => Option.some(model.coverage.grid), toParentMessage: (message) => Message.GotCoverageGridMessage({ message }), }); diff --git a/apps/demo/src/data-grid/update.ts b/apps/demo/src/data-grid/update.ts index a80282c..b4e3b12 100644 --- a/apps/demo/src/data-grid/update.ts +++ b/apps/demo/src/data-grid/update.ts @@ -1,6 +1,6 @@ import { Effect, Option } from "effect"; import { Command, Update } from "foldkit"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { DataGrid } from "@foldworks/data-grid"; @@ -20,7 +20,7 @@ const Save = Command.define("SaveDemoGridEdits", { const foldGrid = Update.foldChild({ update: DataGrid.update, read: (model: Model) => Option.some(model.grid), - write: (model, grid) => evo(model, { grid: () => grid }), + write: (model, grid) => modifyFields(model, { grid: () => grid }), toParentMessage: (message) => Message.GotGridMessage({ message }), foldOutMessage: (submission: DataGrid.OutMessage) => (model: Model) => ({ model, @@ -31,7 +31,7 @@ const foldGrid = Update.foldChild({ const foldCoverageGrid = Update.foldChild({ update: DataGrid.update, read: (model: Model) => Option.some(model.coverage.grid), - write: (model, grid) => evo(model, { coverage: (coverage) => ({ ...coverage, grid }) }), + write: (model, grid) => modifyFields(model, { coverage: (coverage) => ({ ...coverage, grid }) }), toParentMessage: (message) => Message.GotCoverageGridMessage({ message }), // The coverage matrix is read-only, so it never submits edits. foldOutMessage: () => (model: Model) => ({ model }), @@ -42,10 +42,10 @@ export const update = (model: Model, message: Message): Update.Return foldGrid(model, gridMessage), GotCoverageGridMessage: ({ message: gridMessage }) => foldCoverageGrid(model, gridMessage), ChangedCoverageSearch: ({ search }) => ({ - model: evo(model, { coverage: (coverage) => ({ ...coverage, search }) }), + model: modifyFields(model, { coverage: (coverage) => ({ ...coverage, search }) }), }), ToggledCoverageGaps: ({ gapsOnly }) => ({ - model: evo(model, { coverage: (coverage) => ({ ...coverage, gapsOnly }) }), + model: modifyFields(model, { coverage: (coverage) => ({ ...coverage, gapsOnly }) }), }), ChangedEditingMode: ({ mode }) => model.grid.drafts.length || diff --git a/apps/demo/src/form-builder/field-types.ts b/apps/demo/src/form-builder/field-types.ts index 0882ed5..3f7b940 100644 --- a/apps/demo/src/form-builder/field-types.ts +++ b/apps/demo/src/form-builder/field-types.ts @@ -10,10 +10,7 @@ import { TextCursorInput, type LucideIconData, } from "@lucide/icons"; -import { - defineFieldTypes, - type FieldTypeDefinition, -} from "@foldworks/form-builder"; +import { defineFieldTypes, type FieldTypeDefinition } from "@foldworks/form-builder"; import { Message } from "./message"; import type { FieldKind, FormField } from "./model"; @@ -48,20 +45,17 @@ const markdownPreview = (source: string): MarkdownPreview => { return preview; }; -const makeField = ( - id: string, - type: FieldKind, - label: string, -): FormField => ({ +const makeField = (id: string, type: FieldKind, label: string): FormField => ({ id, type, label, description: "", required: false, options: type === "singleSelect" ? ["Option one", "Option two"] : [], - content: type === "content" - ? "# Important information\n\nAdd helpful content for this participant to review." - : "", + content: + type === "content" + ? "# Important information\n\nAdd helpful content for this participant to review." + : "", }); const inputAttributes = (mode: "Editor" | "Runner") => @@ -87,15 +81,13 @@ const definitions = { palette: { label: "Long text", description: "A multi-line answer" }, create: (id) => makeField(id, "longText", "Long answer"), render: ({ mode, value, onInput }, h) => - h.textarea( - [ - h.Class(className(formStyles.previewInput, formStyles.previewTextarea)), - h.Disabled(inputAttributes(mode).disabled), - h.OnInput(onInput), - h.Placeholder("Enter a detailed answer"), - ], - [value], - ), + h.textarea([ + h.Class(className(formStyles.previewInput, formStyles.previewTextarea)), + h.Disabled(inputAttributes(mode).disabled), + h.OnInput(onInput), + h.Placeholder("Enter a detailed answer"), + h.Value(value), + ]), }, singleSelect: { icon: CircleDot, @@ -121,15 +113,18 @@ const definitions = { palette: { label: "Checkbox", description: "An acknowledgment" }, create: (id) => makeField(id, "checkbox", "I agree"), render: ({ field, mode, value, onInput }, h) => - h.label([h.Class(className(formStyles.checkboxRow))], [ - h.input([ - h.Type("checkbox"), - h.Checked(value === "true"), - h.Disabled(inputAttributes(mode).disabled), - h.OnClick(onInput(value === "true" ? "false" : "true")), - ]), - h.span([], [field.label]), - ]), + h.label( + [h.Class(className(formStyles.checkboxRow))], + [ + h.input([ + h.Type("checkbox"), + h.Checked(value === "true"), + h.Disabled(inputAttributes(mode).disabled), + h.OnClick(onInput(value === "true" ? "false" : "true")), + ]), + h.span([], [field.label]), + ], + ), }, date: { icon: CalendarDays, @@ -151,39 +146,50 @@ const definitions = { render: ({ field }, h) => { const preview = markdownPreview(field.content); if (preview._tag === "Invalid") { - return h.div([h.Class(className(formStyles.contentBlock))], [ - h.p([h.Class(className(formStyles.markdownError))], [ - `Markdown preview unavailable: ${preview.message}`, - ]), - h.pre([h.Class(className(formStyles.markdownSource))], [field.content]), - ]); + return h.div( + [h.Class(className(formStyles.contentBlock))], + [ + h.p( + [h.Class(className(formStyles.markdownError))], + [`Markdown preview unavailable: ${preview.message}`], + ), + h.pre([h.Class(className(formStyles.markdownSource))], [field.content]), + ], + ); } - return h.div([h.Class(className(formStyles.contentBlock))], [ - Markdown.view(preview.document, { - views: { - Heading: ({ level }, content) => { - const attributes = [h.Class(className(formStyles.contentHeading))]; - switch (level) { - case 1: return h.h1(attributes, content); - case 2: return h.h2(attributes, content); - case 3: return h.h3(attributes, content); - case 4: return h.h4(attributes, content); - case 5: return h.h5(attributes, content); - case 6: return h.h6(attributes, content); - } + return h.div( + [h.Class(className(formStyles.contentBlock))], + [ + Markdown.view(preview.document, { + views: { + Heading: ({ level }, content) => { + const attributes = [h.Class(className(formStyles.contentHeading))]; + switch (level) { + case 1: + return h.h1(attributes, content); + case 2: + return h.h2(attributes, content); + case 3: + return h.h3(attributes, content); + case 4: + return h.h4(attributes, content); + case 5: + return h.h5(attributes, content); + case 6: + return h.h6(attributes, content); + } + }, + Paragraph: (_paragraph, content) => + h.p([h.Class(className(formStyles.contentParagraph))], content), + InlineCode: ({ value }) => + h.code([h.Class(className(formStyles.markdownInlineCode))], [value]), + CodeBlock: ({ value }) => + h.pre([h.Class(className(formStyles.markdownCodeBlock))], [h.code([], [value])]), }, - Paragraph: (_paragraph, content) => - h.p([h.Class(className(formStyles.contentParagraph))], content), - InlineCode: ({ value }) => - h.code([h.Class(className(formStyles.markdownInlineCode))], [value]), - CodeBlock: ({ value }) => - h.pre([h.Class(className(formStyles.markdownCodeBlock))], [ - h.code([], [value]), - ]), - }, - }), - ]); + }), + ], + ); }, }, } satisfies Record< diff --git a/apps/demo/src/form-builder/subscriptions.ts b/apps/demo/src/form-builder/subscriptions.ts index 0877442..627e430 100644 --- a/apps/demo/src/form-builder/subscriptions.ts +++ b/apps/demo/src/form-builder/subscriptions.ts @@ -1,3 +1,4 @@ +import { Option } from "effect"; import { Subscription } from "foldkit"; import { FormBuilder } from "@foldworks/form-builder"; @@ -6,7 +7,7 @@ import { Message } from "./message"; import type { Model } from "./editor-model"; const interaction = Subscription.lift(FormBuilder.subscriptions)({ - toChildModel: (model) => model.interaction, + read: (model) => Option.some(model.interaction), toParentMessage: (message) => Message.GotInteractionMessage({ message }), }); diff --git a/apps/demo/src/form-builder/update.ts b/apps/demo/src/form-builder/update.ts index ec49f85..436a7d2 100644 --- a/apps/demo/src/form-builder/update.ts +++ b/apps/demo/src/form-builder/update.ts @@ -1,6 +1,6 @@ import { Effect, Option, Schema as S } from "effect"; import { Command, File, Update } from "foldkit"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { FormBuilder, @@ -40,20 +40,21 @@ import type { Model } from "./editor-model"; type UpdateReturn = Update.Return; type ChildUpdateReturn = Update.ReturnWithOutMessage; -type ImportResult = Extract; +type ImportResult = Extract< + Message, + { + readonly _tag: "CompletedImportDocument" | "CancelledImportDocument" | "FailedImportDocument"; + } +>; const ExportDocument = Command.define("ExportFormDocument", { args: { filename: S.String, json: S.String }, messages: [Message.CompletedExportDocument], - execute: ({ filename, json }) => Effect.sync(() => { - downloadJson(filename, json); - return Message.CompletedExportDocument(); - }), + execute: ({ filename, json }) => + Effect.sync(() => { + downloadJson(filename, json); + return Message.CompletedExportDocument(); + }), }); const ImportDocument = Command.define("ImportFormDocument", { @@ -63,23 +64,29 @@ const ImportDocument = Command.define("ImportFormDocument", { Message.CancelledImportDocument, Message.FailedImportDocument, ], - execute: () => File.select(["application/json", ".json"]).pipe( - Effect.flatMap((selected): Effect.Effect => { - if (Option.isNone(selected)) return Effect.succeed(Message.CancelledImportDocument()); - return File.readAsText(selected.value).pipe( - Effect.map((json): ImportResult => Message.CompletedImportDocument({ json })), - Effect.catch(() => Effect.succeed(Message.FailedImportDocument({ - reason: "The selected file could not be read.", - }))), - ); - }), - ), + execute: () => + File.select(["application/json", ".json"]).pipe( + Effect.flatMap((selected): Effect.Effect => { + if (Option.isNone(selected)) return Effect.succeed(Message.CancelledImportDocument()); + return File.readAsText(selected.value).pipe( + Effect.map((json): ImportResult => Message.CompletedImportDocument({ json })), + Effect.catch(() => + Effect.succeed( + Message.FailedImportDocument({ + reason: "The selected file could not be read.", + }), + ), + ), + ); + }), + ), }); const previewPages = (model: Model): ReadonlyArray => model.document.sections - .filter((section) => - model.previewActorId === "__journey__" || section.actorId === model.previewActorId, + .filter( + (section) => + model.previewActorId === "__journey__" || section.actorId === model.previewActorId, ) .flatMap((section) => section.pages); @@ -101,12 +108,12 @@ const markPageContentViewed = (model: Model, pageId: string): Model => { })); return additions.length === 0 ? model - : evo(model, { contentViews: (views) => [...views, ...additions] }); + : modifyFields(model, { contentViews: (views) => [...views, ...additions] }); }; export const loadExample = (model: Model, exampleId: Model["exampleId"]): Model => { const document = model.documents[exampleId]; - return evo(model, { + return modifyFields(model, { exampleId: () => exampleId, outlineTree: () => initOutline(document), document: () => document, @@ -127,10 +134,10 @@ export const setMode = (model: Model, mode: Model["mode"]): Model => { const pages = previewPages(model); const activePageId = pages.some((page) => page.id === model.activePageId) ? model.activePageId - : pages[0]?.id ?? ""; - const next = evo(model, { + : (pages[0]?.id ?? ""); + const next = modifyFields(model, { mode: () => mode, - outlineTree: tree => ({ ...tree, editingId: null }), + outlineTree: (tree) => ({ ...tree, editingId: null }), history: (history) => History.breakCoalescing(history), activePageId: () => activePageId, revision: (value) => value + 1, @@ -138,70 +145,74 @@ export const setMode = (model: Model, mode: Model["mode"]): Model => { return mode === "Preview" ? markPageContentViewed(next, activePageId) : next; }; -const commitDrop = (outMessage: FormBuilder.OutMessage): Update.Step => - (model) => FormBuilder.OutMessage.match(outMessage, { - Cancelled: () => ({ model }), - Reordered: (reordered) => { - const result = applyReorder({ - document: model.document, - reordered: FormBuilder.OutMessage.Reordered(reordered), - operations: { insertField, moveItem }, - createFromPalette: (type) => - isFieldKind(type) ? createField(type, model.nextId) : undefined, - }); - if (result === undefined) return { model }; - if (result._tag === "Inserted") { +const commitDrop = + (outMessage: FormBuilder.OutMessage): Update.Step => + (model) => + FormBuilder.OutMessage.match(outMessage, { + Cancelled: () => ({ model }), + Reordered: (reordered) => { + const result = applyReorder({ + document: model.document, + reordered: FormBuilder.OutMessage.Reordered(reordered), + operations: { insertField, moveItem }, + createFromPalette: (type) => + isFieldKind(type) ? createField(type, model.nextId) : undefined, + }); + if (result === undefined) return { model }; + if (result._tag === "Inserted") { + return { + model: modifyFields(model, { + document: () => result.document, + selectedItem: () => Option.some({ kind: "Field", id: result.field.id }), + activePageId: () => result.location.pageId, + nextId: (value) => value + 1, + revision: (value) => value + 1, + announcement: () => `${result.field.label} added.`, + }), + }; + } return { - model: evo(model, { + model: modifyFields(model, { document: () => result.document, - selectedItem: () => Option.some({ kind: "Field", id: result.field.id }), - activePageId: () => result.location.pageId, - nextId: (value) => value + 1, revision: (value) => value + 1, - announcement: () => `${result.field.label} added.`, + announcement: () => `${result.item.kind} moved.`, }), }; - } - return { - model: evo(model, { - document: () => result.document, - revision: (value) => value + 1, - announcement: () => `${result.item.kind} moved.`, - }), - }; - }, - }); + }, + }); const foldInteraction = Update.foldChild({ update: FormBuilder.update, read: (model: Model) => Option.some(model.interaction), - write: (model, interaction) => evo(model, { interaction: () => interaction }), + write: (model, interaction) => modifyFields(model, { interaction: () => interaction }), toParentMessage: (message) => Message.GotInteractionMessage({ message }), foldOutMessage: commitDrop, }); const restoreFromHistory = (model: Model, direction: "Undo" | "Redo"): UpdateReturn => { - const step = direction === "Undo" - ? History.undo(model.history, model.document) - : History.redo(model.history, model.document); + const step = + direction === "Undo" + ? History.undo(model.history, model.document) + : History.redo(model.history, model.document); if (step === undefined) return { model }; const selection = Option.getOrUndefined(model.selectedItem); - const keepsSelection = selection !== undefined && ( - selection.kind === "Section" + const keepsSelection = + selection !== undefined && + (selection.kind === "Section" ? findSection(step.value, selection.id) !== undefined : selection.kind === "Page" ? findPage(step.value, selection.id) !== undefined - : findField(step.value, selection.id) !== undefined - ); + : findField(step.value, selection.id) !== undefined); return { - model: evo(model, { + model: modifyFields(model, { document: () => step.value, history: () => step.history, interaction: () => FormBuilder.init(), - selectedItem: () => keepsSelection ? model.selectedItem : Option.none(), - activePageId: () => findPage(step.value, model.activePageId) === undefined - ? step.value.sections[0]?.pages[0]?.id ?? "" - : model.activePageId, + selectedItem: () => (keepsSelection ? model.selectedItem : Option.none()), + activePageId: () => + findPage(step.value, model.activePageId) === undefined + ? (step.value.sections[0]?.pages[0]?.id ?? "") + : model.activePageId, nextId: () => nextFormId(step.value), revision: (value) => value + 1, announcement: () => `${direction} completed for the form.`, @@ -214,11 +225,16 @@ const coalescingKey = (model: Model, message: Message): string | undefined => { if (selection === undefined) return undefined; const prefix = `form:${selection.kind}:${selection.id}`; switch (message._tag) { - case "ChangedItemTitle": return `${prefix}:title`; - case "ChangedItemDescription": return `${prefix}:description`; - case "ChangedFieldContent": return `${prefix}:content`; - case "ChangedFieldOptions": return `${prefix}:options`; - default: return undefined; + case "ChangedItemTitle": + return `${prefix}:title`; + case "ChangedItemDescription": + return `${prefix}:description`; + case "ChangedFieldContent": + return `${prefix}:content`; + case "ChangedFieldOptions": + return `${prefix}:options`; + default: + return undefined; } }; @@ -237,60 +253,97 @@ const foldSettingsPane = Update.foldChild({ const updateCore = (model: Model, message: Message): UpdateReturn => Message.match(message, { - ToggledStructureView: () => ({ model: { ...model, structureCards: !model.structureCards, outlineTree: { ...model.outlineTree, editingId: null } } }), - OutlineTree: ({ message }) => Update.foldChild({ - update: (tree: Tree.Model, event: Tree.Message) => Tree.update(tree, event, outlineConfig(model.document)), - read: (parent: Model) => Option.some(parent.outlineTree), - write: (parent, outlineTree) => ({ ...parent, outlineTree }), - toParentMessage: event => Message.OutlineTree({ message: event }), - foldOutMessage: (event: Tree.OutMessage) => (parent: Model): UpdateReturn => { - const nodes = outlineNodes(parent.document); - const node = nodes.find(node => node.id === event.id); - if (!node) return { model: parent }; - if (event._tag === "Selected") return updateCore(parent, Message.SelectedItem({ kind: node.kind, id: node.sourceId })); - if (event._tag === "Renamed") return { model: { ...parent, document: node.kind === "Section" - ? updateSection(parent.document, node.sourceId, section => ({ ...section, title: event.label })) - : updatePage(parent.document, node.sourceId, page => ({ ...page, title: event.label })) } }; - const siblings = nodes.filter(item => item.parentId === node.parentId); - const sourceIndex = siblings.findIndex(item => item.id === node.id); - // FormBuilder locations refer to the original list; Tree indices refer to the list after removal. - const index = event.index + (node.parentId === event.parentId && sourceIndex < event.index ? 1 : 0); - const destination = nodes.find(item => item.id === event.parentId); - const document = node.kind === "Section" - ? moveItem(parent.document, "Section", node.sourceId, { kind: "Section", index }) - : destination ? moveItem(parent.document, "Page", node.sourceId, { kind: "Page", sectionId: destination.sourceId, index }) : undefined; - return { model: document ? { ...parent, document } : parent }; + ToggledStructureView: () => ({ + model: { + ...model, + structureCards: !model.structureCards, + outlineTree: { ...model.outlineTree, editingId: null }, }, - })(model, message), + }), + OutlineTree: ({ message }) => + Update.foldChild({ + update: (tree: Tree.Model, event: Tree.Message) => + Tree.update(tree, event, outlineConfig(model.document)), + read: (parent: Model) => Option.some(parent.outlineTree), + write: (parent, outlineTree) => ({ ...parent, outlineTree }), + toParentMessage: (event) => Message.OutlineTree({ message: event }), + foldOutMessage: + (event: Tree.OutMessage) => + (parent: Model): UpdateReturn => { + const nodes = outlineNodes(parent.document); + const node = nodes.find((node) => node.id === event.id); + if (!node) return { model: parent }; + if (event._tag === "Selected") + return updateCore( + parent, + Message.SelectedItem({ kind: node.kind, id: node.sourceId }), + ); + if (event._tag === "Renamed") + return { + model: { + ...parent, + document: + node.kind === "Section" + ? updateSection(parent.document, node.sourceId, (section) => ({ + ...section, + title: event.label, + })) + : updatePage(parent.document, node.sourceId, (page) => ({ + ...page, + title: event.label, + })), + }, + }; + const siblings = nodes.filter((item) => item.parentId === node.parentId); + const sourceIndex = siblings.findIndex((item) => item.id === node.id); + // FormBuilder locations refer to the original list; Tree indices refer to the list after removal. + const index = + event.index + (node.parentId === event.parentId && sourceIndex < event.index ? 1 : 0); + const destination = nodes.find((item) => item.id === event.parentId); + const document = + node.kind === "Section" + ? moveItem(parent.document, "Section", node.sourceId, { kind: "Section", index }) + : destination + ? moveItem(parent.document, "Page", node.sourceId, { + kind: "Page", + sectionId: destination.sourceId, + index, + }) + : undefined; + return { model: document ? { ...parent, document } : parent }; + }, + })(model, message), OutlinePane: ({ message }) => foldOutlinePane(model, message), SettingsPane: ({ message }) => foldSettingsPane(model, message), CompletedExportDocument: () => ({ - model: evo(model, { announcement: () => "Form JSON exported." }), + model: modifyFields(model, { announcement: () => "Form JSON exported." }), }), CancelledImportDocument: () => ({ model }), FailedImportDocument: ({ reason }) => ({ - model: evo(model, { announcement: () => reason }), + model: modifyFields(model, { announcement: () => reason }), }), ClickedUndo: () => restoreFromHistory(model, "Undo"), ClickedRedo: () => restoreFromHistory(model, "Redo"), ClickedExportDocument: () => ({ model, - commands: [ExportDocument({ - filename: `${model.document.id}.form.json`, - json: serializeFormExport(model.document), - })], + commands: [ + ExportDocument({ + filename: `${model.document.id}.form.json`, + json: serializeFormExport(model.document), + }), + ], }), ClickedImportDocument: () => ({ model, commands: [ImportDocument({})] }), CompletedImportDocument: ({ json }) => { const document = parseFormExport(json); return document === undefined ? { - model: evo(model, { + model: modifyFields(model, { announcement: () => "That file is not a valid form export.", }), } : { - model: evo(model, { + model: modifyFields(model, { document: () => document, outlineTree: () => initOutline(document), interaction: () => FormBuilder.init(), @@ -307,15 +360,16 @@ const updateCore = (model: Model, message: Message): UpdateReturn => SelectedExample: () => ({ model }), SelectedMode: () => ({ model }), SelectedItem: ({ kind, id }) => ({ - model: evo(model, { + model: modifyFields(model, { interaction: () => FormBuilder.init(), selectedItem: () => Option.some({ kind, id }), history: (history) => History.breakCoalescing(history), - activePageId: (current) => kind === "Page" - ? id - : kind === "Field" - ? locateField(model.document, id)?.page.id ?? current - : current, + activePageId: (current) => + kind === "Page" + ? id + : kind === "Field" + ? (locateField(model.document, id)?.page.id ?? current) + : current, }), }), SelectedPreviewActor: ({ actorId }) => { @@ -324,10 +378,13 @@ const updateCore = (model: Model, message: Message): UpdateReturn => .flatMap((section) => section.pages); const pageId = pages[0]?.id ?? ""; return { - model: markPageContentViewed(evo(model, { - previewActorId: () => actorId, - activePageId: () => pageId, - }), pageId), + model: markPageContentViewed( + modifyFields(model, { + previewActorId: () => actorId, + activePageId: () => pageId, + }), + pageId, + ), }; }, ClickedAddSection: () => { @@ -339,151 +396,199 @@ const updateCore = (model: Model, message: Message): UpdateReturn => { kind: "Section", index: model.document.sections.length }, { id, actorId: actor.id, title: "New section", description: "", pages: [] }, ); - return document === undefined ? { model } : { - model: evo(model, { - document: () => document, - selectedItem: () => Option.some({ kind: "Section", id }), - nextId: (value) => value + 1, - revision: (value) => value + 1, - }), - }; + return document === undefined + ? { model } + : { + model: modifyFields(model, { + document: () => document, + selectedItem: () => Option.some({ kind: "Section", id }), + nextId: (value) => value + 1, + revision: (value) => value + 1, + }), + }; }, ClickedAddPage: ({ sectionId }) => { const section = findSection(model.document, sectionId); if (section === undefined) return { model }; const id = `form-page-${model.nextId}`; - const document = insertPage(model.document, { - kind: "Page", - sectionId, - index: section.pages.length, - }, { id, title: "Untitled page", description: "", fields: [] }); - return document === undefined ? { model } : { - model: evo(model, { - document: () => document, - selectedItem: () => Option.some({ kind: "Page", id }), - activePageId: () => id, - nextId: (value) => value + 1, - revision: (value) => value + 1, - }), - }; + const document = insertPage( + model.document, + { + kind: "Page", + sectionId, + index: section.pages.length, + }, + { id, title: "Untitled page", description: "", fields: [] }, + ); + return document === undefined + ? { model } + : { + model: modifyFields(model, { + document: () => document, + selectedItem: () => Option.some({ kind: "Page", id }), + activePageId: () => id, + nextId: (value) => value + 1, + revision: (value) => value + 1, + }), + }; }, ClickedAddField: ({ pageId, fieldType }) => { const page = findPage(model.document, pageId); const paletteType = paletteTypeFromId(paletteItemId(fieldType)); - const kind = paletteType !== undefined && isFieldKind(paletteType) - ? paletteType - : undefined; + const kind = paletteType !== undefined && isFieldKind(paletteType) ? paletteType : undefined; if (page === undefined || kind === undefined) return { model }; const field = createField(kind, model.nextId); - const document = insertField(model.document, { - kind: "Field", - pageId, - index: page.fields.length, - }, field); - return document === undefined ? { model } : { - model: evo(model, { - document: () => document, - interaction: () => FormBuilder.init(), - selectedItem: () => Option.some({ kind: "Field", id: field.id }), - activePageId: () => pageId, - nextId: (value) => value + 1, - revision: (value) => value + 1, - }), - }; + const document = insertField( + model.document, + { + kind: "Field", + pageId, + index: page.fields.length, + }, + field, + ); + return document === undefined + ? { model } + : { + model: modifyFields(model, { + document: () => document, + interaction: () => FormBuilder.init(), + selectedItem: () => Option.some({ kind: "Field", id: field.id }), + activePageId: () => pageId, + nextId: (value) => value + 1, + revision: (value) => value + 1, + }), + }; }, - ChangedItemTitle: ({ value }) => Option.match(model.selectedItem, { - onNone: () => ({ model }), - onSome: (selection) => ({ - model: evo(model, { - document: (document) => selection.kind === "Section" - ? updateSection(document, selection.id, (section) => ({ ...section, title: value })) - : selection.kind === "Page" - ? updatePage(document, selection.id, (page) => ({ ...page, title: value })) - : updateField(document, selection.id, (field) => ({ ...field, label: value })), + ChangedItemTitle: ({ value }) => + Option.match(model.selectedItem, { + onNone: () => ({ model }), + onSome: (selection) => ({ + model: modifyFields(model, { + document: (document) => + selection.kind === "Section" + ? updateSection(document, selection.id, (section) => ({ ...section, title: value })) + : selection.kind === "Page" + ? updatePage(document, selection.id, (page) => ({ ...page, title: value })) + : updateField(document, selection.id, (field) => ({ ...field, label: value })), + }), }), }), - }), - ChangedItemDescription: ({ value }) => Option.match(model.selectedItem, { - onNone: () => ({ model }), - onSome: (selection) => ({ - model: evo(model, { - document: (document) => selection.kind === "Section" - ? updateSection(document, selection.id, (section) => ({ ...section, description: value })) - : selection.kind === "Page" - ? updatePage(document, selection.id, (page) => ({ ...page, description: value })) - : updateField(document, selection.id, (field) => ({ ...field, description: value })), + ChangedItemDescription: ({ value }) => + Option.match(model.selectedItem, { + onNone: () => ({ model }), + onSome: (selection) => ({ + model: modifyFields(model, { + document: (document) => + selection.kind === "Section" + ? updateSection(document, selection.id, (section) => ({ + ...section, + description: value, + })) + : selection.kind === "Page" + ? updatePage(document, selection.id, (page) => ({ ...page, description: value })) + : updateField(document, selection.id, (field) => ({ + ...field, + description: value, + })), + }), }), }), - }), - ChangedSectionActor: ({ actorId }) => Option.match(model.selectedItem, { - onNone: () => ({ model }), - onSome: (selection) => ({ - model: selection.kind !== "Section" ? model : evo(model, { - document: (document) => updateSection(document, selection.id, (section) => ({ - ...section, - actorId, - })), + ChangedSectionActor: ({ actorId }) => + Option.match(model.selectedItem, { + onNone: () => ({ model }), + onSome: (selection) => ({ + model: + selection.kind !== "Section" + ? model + : modifyFields(model, { + document: (document) => + updateSection(document, selection.id, (section) => ({ + ...section, + actorId, + })), + }), }), }), - }), - ChangedFieldRequired: ({ required }) => Option.match(model.selectedItem, { - onNone: () => ({ model }), - onSome: (selection) => ({ - model: selection.kind !== "Field" ? model : evo(model, { - document: (document) => updateField(document, selection.id, (field) => ({ - ...field, - required, - })), + ChangedFieldRequired: ({ required }) => + Option.match(model.selectedItem, { + onNone: () => ({ model }), + onSome: (selection) => ({ + model: + selection.kind !== "Field" + ? model + : modifyFields(model, { + document: (document) => + updateField(document, selection.id, (field) => ({ + ...field, + required, + })), + }), }), }), - }), - ChangedFieldContent: ({ value }) => Option.match(model.selectedItem, { - onNone: () => ({ model }), - onSome: (selection) => ({ - model: selection.kind !== "Field" ? model : evo(model, { - document: (document) => updateField(document, selection.id, (field) => ({ - ...field, - content: value, - })), + ChangedFieldContent: ({ value }) => + Option.match(model.selectedItem, { + onNone: () => ({ model }), + onSome: (selection) => ({ + model: + selection.kind !== "Field" + ? model + : modifyFields(model, { + document: (document) => + updateField(document, selection.id, (field) => ({ + ...field, + content: value, + })), + }), }), }), - }), - ChangedFieldOptions: ({ value }) => Option.match(model.selectedItem, { - onNone: () => ({ model }), - onSome: (selection) => ({ - model: selection.kind !== "Field" ? model : evo(model, { - document: (document) => updateField(document, selection.id, (field) => ({ - ...field, - options: value.split("\n").map((option) => option.trim()).filter(Boolean), - })), + ChangedFieldOptions: ({ value }) => + Option.match(model.selectedItem, { + onNone: () => ({ model }), + onSome: (selection) => ({ + model: + selection.kind !== "Field" + ? model + : modifyFields(model, { + document: (document) => + updateField(document, selection.id, (field) => ({ + ...field, + options: value + .split("\n") + .map((option) => option.trim()) + .filter(Boolean), + })), + }), }), }), - }), - ClickedDeleteItem: () => Option.match(model.selectedItem, { - onNone: () => ({ model }), - onSome: (selection) => { - const document: FormDocument | undefined = selection.kind === "Section" - ? deleteSection(model.document, selection.id) - : selection.kind === "Page" - ? deletePage(model.document, selection.id) - : deleteField(model.document, selection.id); - if (document === undefined) return { model }; - return { - model: evo(model, { - document: () => document, - selectedItem: () => Option.none(), - activePageId: (current) => findPage(document, current) === undefined - ? document.sections[0]?.pages[0]?.id ?? "" - : current, - revision: (value) => value + 1, - }), - }; - }, - }), + ClickedDeleteItem: () => + Option.match(model.selectedItem, { + onNone: () => ({ model }), + onSome: (selection) => { + const document: FormDocument | undefined = + selection.kind === "Section" + ? deleteSection(model.document, selection.id) + : selection.kind === "Page" + ? deletePage(model.document, selection.id) + : deleteField(model.document, selection.id); + if (document === undefined) return { model }; + return { + model: modifyFields(model, { + document: () => document, + selectedItem: () => Option.none(), + activePageId: (current) => + findPage(document, current) === undefined + ? (document.sections[0]?.pages[0]?.id ?? "") + : current, + revision: (value) => value + 1, + }), + }; + }, + }), ClickedReset: () => { const document = exampleForms[model.exampleId]; return { - model: evo(model, { + model: modifyFields(model, { document: () => document, interaction: () => FormBuilder.init(), selectedItem: () => Option.none(), @@ -495,24 +600,25 @@ const updateCore = (model: Model, message: Message): UpdateReturn => }; }, ChangedAnswer: ({ fieldId, value }) => ({ - model: evo(model, { - answers: (answers) => answers.some((answer) => answer.fieldId === fieldId) - ? answers.map((answer) => answer.fieldId === fieldId ? { ...answer, value } : answer) - : [...answers, { fieldId, value }], + model: modifyFields(model, { + answers: (answers) => + answers.some((answer) => answer.fieldId === fieldId) + ? answers.map((answer) => (answer.fieldId === fieldId ? { ...answer, value } : answer)) + : [...answers, { fieldId, value }], }), }), ClickedPreviewPrevious: () => { const pages = previewPages(model); const index = pages.findIndex((page) => page.id === model.activePageId); const pageId = pages[Math.max(0, index - 1)]?.id ?? model.activePageId; - return { model: evo(model, { activePageId: () => pageId }) }; + return { model: modifyFields(model, { activePageId: () => pageId }) }; }, ClickedPreviewNext: () => { const pages = previewPages(model); const index = pages.findIndex((page) => page.id === model.activePageId); const pageId = pages[Math.min(pages.length - 1, index + 1)]?.id ?? model.activePageId; return { - model: markPageContentViewed(evo(model, { activePageId: () => pageId }), pageId), + model: markPageContentViewed(modifyFields(model, { activePageId: () => pageId }), pageId), }; }, }); @@ -538,31 +644,68 @@ export const update = (model: Model, message: Message): ChildUpdateReturn => { } let result = updateCore(model, message); - if (result.model.selectedItem !== model.selectedItem && Option.isSome(result.model.selectedItem)) { + if ( + result.model.selectedItem !== model.selectedItem && + Option.isSome(result.model.selectedItem) + ) { const selection = result.model.selectedItem.value; - const section = result.model.document.sections.find(section => selection.kind === "Section" ? section.id === selection.id - : section.pages.some(page => selection.kind === "Page" ? page.id === selection.id : page.fields.some(field => field.id === selection.id))); - const page = section?.pages.find(page => selection.kind === "Page" ? page.id === selection.id : page.fields.some(field => field.id === selection.id)); - const id = selection.kind === "Section" && section ? `section:${section.id}` : page ? `page:${page.id}` : null; - if (id && section) result = { ...result, model: { ...result.model, outlineTree: { - ...result.model.outlineTree, selectedId: id, activeId: id, editingId: null, - expandedIds: [...new Set([...result.model.outlineTree.expandedIds, `section:${section.id}`])], - } } }; + const section = result.model.document.sections.find((section) => + selection.kind === "Section" + ? section.id === selection.id + : section.pages.some((page) => + selection.kind === "Page" + ? page.id === selection.id + : page.fields.some((field) => field.id === selection.id), + ), + ); + const page = section?.pages.find((page) => + selection.kind === "Page" + ? page.id === selection.id + : page.fields.some((field) => field.id === selection.id), + ); + const id = + selection.kind === "Section" && section + ? `section:${section.id}` + : page + ? `page:${page.id}` + : null; + if (id && section) + result = { + ...result, + model: { + ...result.model, + outlineTree: { + ...result.model.outlineTree, + selectedId: id, + activeId: id, + editingId: null, + expandedIds: [ + ...new Set([...result.model.outlineTree.expandedIds, `section:${section.id}`]), + ], + }, + }, + }; } - if (result.model.document !== model.document) result = { ...result, model: { ...result.model, - outlineTree: Tree.reconcile(result.model.outlineTree, outlineNodes(result.model.document)), - } }; + if (result.model.document !== model.document) + result = { + ...result, + model: { + ...result.model, + outlineTree: Tree.reconcile(result.model.outlineTree, outlineNodes(result.model.document)), + }, + }; if (result.model.document === model.document) return result; const traversedHistory = message._tag === "ClickedUndo" || message._tag === "ClickedRedo"; const key = coalescingKey(model, message); - const next = evo(result.model, { - history: () => traversedHistory - ? result.model.history - : History.record( - model.history, - model.document, - key === undefined ? {} : { coalescingKey: key }, - ), + const next = modifyFields(result.model, { + history: () => + traversedHistory + ? result.model.history + : History.record( + model.history, + model.document, + key === undefined ? {} : { coalescingKey: key }, + ), documents: (documents) => ({ ...documents, [result.model.exampleId]: result.model.document, diff --git a/apps/demo/src/generative-ui/mcp-app.ts b/apps/demo/src/generative-ui/mcp-app.ts index 19914f2..95f344e 100644 --- a/apps/demo/src/generative-ui/mcp-app.ts +++ b/apps/demo/src/generative-ui/mcp-app.ts @@ -1,8 +1,8 @@ import { ActionIntent, Spec, render } from "@foldworks/generative-ui/core"; import { connectMcpApp, sendActionIntent } from "@foldworks/generative-ui/mcp-app"; import { foldworksRegistry } from "@foldworks/generative-ui/foldworks"; -import { Schema as S } from "effect"; -import { Port, Runtime, Subscription } from "foldkit"; +import { Effect, Schema as S } from "effect"; +import { Command, Port, Runtime, Subscription, type Update } from "foldkit"; import type { Html, HtmlBuilder } from "foldkit/html"; import { defineMessageUnion } from "foldkit/message"; @@ -18,6 +18,7 @@ type Surface = typeof Surface.Type; const Message = defineMessageUnion({ ReceivedSurface: { surface: Surface }, TriggeredAction: { intent: ActionIntent }, + CompletedEmitAction: {}, }); type Message = typeof Message.Type; @@ -29,8 +30,15 @@ const ports = { outbound: { action: Port.outbound(ActionIntent) }, }; +const EmitAction = Command.define("McpAppEmitAction", { + args: { intent: ActionIntent }, + messages: [Message.CompletedEmitAction], + execute: ({ intent }) => + Port.emit(ports.outbound.action, intent).pipe(Effect.as(Message.CompletedEmitAction())), +}); + const subscriptions = Subscription.make()(() => ({ - surface: Port.subscription(ports.inbound.surface, (surface) => + surface: Port.subscriptionEntry(ports.inbound.surface, (surface) => Message.ReceivedSurface({ surface }), ), })); @@ -61,12 +69,13 @@ const program = Runtime.makeElement({ container: root, ports, init: () => ({ model: { surface: { _tag: "Waiting" as const } } }), - update: (model: Model, message: Message) => - Message.match(message, { + update: (model: Model, message: Message): Update.Return => + Message.match>(message, { + CompletedEmitAction: () => ({ model }), ReceivedSurface: ({ surface }) => ({ model: { surface } }), TriggeredAction: ({ intent }) => ({ model, - commands: [Port.emit(ports.outbound.action, intent)], + commands: [EmitAction({ intent })], }), }), view, diff --git a/apps/demo/src/outliner/update.ts b/apps/demo/src/outliner/update.ts index 1c2dd56..27fb996 100644 --- a/apps/demo/src/outliner/update.ts +++ b/apps/demo/src/outliner/update.ts @@ -1,6 +1,6 @@ import { Option } from "effect"; import { Update } from "foldkit"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { Outliner, find, updateItem } from "@foldworks/outliner"; import { suggestionsAt } from "./mentions"; @@ -12,7 +12,7 @@ const foldOutliner = Update.foldChild({ update: (outline: Outliner.Model, message: Outliner.Message) => Outliner.update(outline, message, outlinePolicy(outline.items)), read: (model: Model) => Option.some(model.outline), - write: (model, outline) => evo(model, { outline: () => outline }), + write: (model, outline) => modifyFields(model, { outline: () => outline }), toParentMessage: (message) => Message.GotOutlinerMessage({ message }), }); @@ -70,6 +70,8 @@ export const update = (model: Model, message: Message): UpdateReturn => Message.match(message, { GotOutlinerMessage: ({ message: childMessage }) => suggest(foldOutliner(model, childMessage), childMessage), - ToggledCheckboxes: () => ({ model: evo(model, { showCheckboxes: (value) => !value }) }), + ToggledCheckboxes: () => ({ + model: modifyFields(model, { showCheckboxes: (value) => !value }), + }), ToggledDone: ({ id }) => toggleDone(model, id), }); diff --git a/apps/demo/src/query-builder/subscriptions.ts b/apps/demo/src/query-builder/subscriptions.ts index 94916b6..3dfdb21 100644 --- a/apps/demo/src/query-builder/subscriptions.ts +++ b/apps/demo/src/query-builder/subscriptions.ts @@ -1,3 +1,4 @@ +import { Option } from "effect"; import { Subscription } from "foldkit"; import { QueryBuilder } from "@foldworks/query-builder"; @@ -6,7 +7,7 @@ import { Message } from "./message"; import type { Model } from "./model"; const interaction = Subscription.lift(QueryBuilder.subscriptions)({ - toChildModel: (model) => model.builder, + read: (model) => Option.some(model.builder), toParentMessage: (message) => Message.GotQueryBuilderMessage({ message }), }); diff --git a/apps/demo/src/query-builder/update.ts b/apps/demo/src/query-builder/update.ts index 1213596..b434c57 100644 --- a/apps/demo/src/query-builder/update.ts +++ b/apps/demo/src/query-builder/update.ts @@ -1,6 +1,6 @@ import { Option } from "effect"; import { Update } from "foldkit"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { QueryBuilder, validate } from "@foldworks/query-builder"; @@ -13,11 +13,12 @@ const foldQueryBuilder = Update.foldChild({ read: (model: Model) => Option.some(model.builder), write: (model, builder) => { const validation = validate(builder.query, { attributes, validateValue }); - return evo(model, { + return modifyFields(model, { builder: () => builder, - announcement: () => validation.isValid - ? "Query updated and valid." - : `Query updated with ${validation.issues.length} validation ${validation.issues.length === 1 ? "issue" : "issues"}.`, + announcement: () => + validation.isValid + ? "Query updated and valid." + : `Query updated with ${validation.issues.length} validation ${validation.issues.length === 1 ? "issue" : "issues"}.`, }); }, toParentMessage: (message) => Message.GotQueryBuilderMessage({ message }), @@ -25,6 +26,5 @@ const foldQueryBuilder = Update.foldChild({ export const update = (model: Model, message: Message): Update.Return => Message.match(message, { - GotQueryBuilderMessage: ({ message: childMessage }) => - foldQueryBuilder(model, childMessage), + GotQueryBuilderMessage: ({ message: childMessage }) => foldQueryBuilder(model, childMessage), }); diff --git a/apps/demo/src/statechart/subscriptions.ts b/apps/demo/src/statechart/subscriptions.ts index 749adee..f5891f5 100644 --- a/apps/demo/src/statechart/subscriptions.ts +++ b/apps/demo/src/statechart/subscriptions.ts @@ -1,3 +1,4 @@ +import { Option } from "effect"; import { Subscription } from "foldkit"; import { Diagram } from "@foldworks/diagram"; @@ -6,6 +7,6 @@ import { Message } from "./message"; import type { Model } from "./model"; export const subscriptions = Subscription.lift(Diagram.subscriptions)({ - toChildModel: (model) => model.canvas, + read: (model) => Option.some(model.canvas), toParentMessage: (message) => Message.GotCanvasMessage({ message }), }); diff --git a/apps/demo/src/ui-kit/update.ts b/apps/demo/src/ui-kit/update.ts index 7191529..4b47bce 100644 --- a/apps/demo/src/ui-kit/update.ts +++ b/apps/demo/src/ui-kit/update.ts @@ -2,7 +2,7 @@ import * as Desktop from "./desktop"; import { Command, Update } from "foldkit"; import { Effect, Option, Schema as S } from "effect"; import { CodeBlock, Stateful } from "@foldworks/ui"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { Message } from "./message"; import { catalogSnippet, type Model } from "./model"; @@ -200,116 +200,116 @@ export const update = (model: Model, message: Message): Update.Return foldCombobox(model, message), GotMultiComboboxMessage: ({ message }) => foldMultiCombobox(model, message), GotToastMessage: ({ message }) => foldToast(model, message), - ChangedName: ({ value }) => ({ model: evo(model, { name: () => value }) }), - ChangedEmail: ({ value }) => ({ model: evo(model, { email: () => value }) }), - ChangedNotes: ({ value }) => ({ model: evo(model, { notes: () => value }) }), + ChangedName: ({ value }) => ({ model: modifyFields(model, { name: () => value }) }), + ChangedEmail: ({ value }) => ({ model: modifyFields(model, { email: () => value }) }), + ChangedNotes: ({ value }) => ({ model: modifyFields(model, { notes: () => value }) }), SelectedAnswerPresentation: ({ value }) => ({ - model: evo(model, { + model: modifyFields(model, { answerPresentation: () => value, announcement: () => value === "value" ? "Showing read-only answers." : "Showing editable controls.", }), }), CommittedReference: ({ value }) => ({ - model: evo(model, { + model: modifyFields(model, { committedReference: () => value, announcement: () => `Reference ${value} committed.`, }), }), ChangedZoom: ({ zoom }) => ({ - model: evo(model, { + model: modifyFields(model, { zoom: () => Math.min(200, Math.max(50, zoom)), announcement: () => `Zoom ${Math.min(200, Math.max(50, zoom))}%.`, }), }), SelectedDepartment: ({ value }) => ({ - model: evo(model, { + model: modifyFields(model, { department: () => value, openComponent: () => "", announcement: () => `${value} department selected.`, }), }), SelectedFoundationStep: ({ value }) => ({ - model: evo(model, { + model: modifyFields(model, { foundationStep: () => value, announcement: () => `${value} step selected.`, }), }), RemovedTool: ({ value }) => ({ - model: evo(model, { + model: modifyFields(model, { selectedTools: (values) => values.filter((selected) => selected !== value), announcement: () => `${value} removed from the selected tools.`, }), }), RequestedToast: ({ variant }) => showToast(variant)(model), SelectedView: ({ value }) => ({ - model: evo(model, { + model: modifyFields(model, { selectedView: () => value, announcement: () => `${value} view selected.`, }), }), ToggledUpdates: ({ isChecked }) => ({ - model: evo(model, { + model: modifyFields(model, { receivesUpdates: () => isChecked, announcement: () => `Product updates ${isChecked ? "enabled" : "disabled"}.`, }), }), ToggledSecurityAlerts: ({ isChecked }) => ({ - model: evo(model, { + model: modifyFields(model, { securityAlerts: () => isChecked, announcement: () => `Security alerts ${isChecked ? "enabled" : "disabled"}.`, }), }), ToggledTerms: ({ isChecked }) => ({ - model: evo(model, { + model: modifyFields(model, { termsAccepted: () => isChecked, announcement: () => `Workspace terms ${isChecked ? "accepted" : "cleared"}.`, }), }), ToggledMixedPermissions: ({ isChecked }) => ({ - model: evo(model, { + model: modifyFields(model, { mixedPermissions: () => isChecked, announcement: () => `Team permissions ${isChecked ? "selected" : "cleared"}.`, }), }), ToggledDetails: ({ isOpen }) => ({ - model: evo(model, { + model: modifyFields(model, { isDetailsOpen: () => isOpen, announcement: () => `Disclosure ${isOpen ? "expanded" : "collapsed"}.`, }), }), ToggledComponent: ({ component, isOpen }) => ({ - model: evo(model, { + model: modifyFields(model, { openComponent: () => (isOpen ? component : ""), announcement: () => `${component} ${isOpen ? "opened" : "closed"}.`, }), }), ChangedSlider: ({ value }) => ({ - model: evo(model, { + model: modifyFields(model, { sliderValue: () => value, announcement: () => `Slider changed to ${value}.`, }), }), ChangedOtp: ({ value }) => ({ - model: evo(model, { otp: () => value }), + model: modifyFields(model, { otp: () => value }), }), ChangedCommandQuery: ({ value }) => ({ - model: evo(model, { commandQuery: () => value }), + model: modifyFields(model, { commandQuery: () => value }), }), SelectedPage: ({ page }) => ({ - model: evo(model, { + model: modifyFields(model, { page: () => page, announcement: () => `Page ${page} selected.`, }), }), SelectedCalendarDay: ({ day }) => ({ - model: evo(model, { + model: modifyFields(model, { selectedCalendarDay: () => day, announcement: () => `September ${day} selected.`, }), }), ClickedAction: ({ action }) => ({ - model: evo(model, { announcement: () => `${action} button selected.` }), + model: modifyFields(model, { announcement: () => `${action} button selected.` }), }), ClickedCopySnippet: () => ({ model, @@ -317,9 +317,14 @@ export const update = (model: Model, message: Message): Update.Return isCopied - ? { model: evo(model, { isSnippetCopied: () => true }), commands: [ClearCopiedSnippet()] } - : { model: evo(model, { announcement: () => "Clipboard access is unavailable." }) }, + ? { + model: modifyFields(model, { isSnippetCopied: () => true }), + commands: [ClearCopiedSnippet()], + } + : { + model: modifyFields(model, { announcement: () => "Clipboard access is unavailable." }), + }, ClearedCopiedSnippet: () => ({ - model: evo(model, { isSnippetCopied: () => false }), + model: modifyFields(model, { isSnippetCopied: () => false }), }), }); diff --git a/apps/demo/src/workbench/subscriptions.ts b/apps/demo/src/workbench/subscriptions.ts index 807ac30..ed6ab71 100644 --- a/apps/demo/src/workbench/subscriptions.ts +++ b/apps/demo/src/workbench/subscriptions.ts @@ -1,10 +1,11 @@ +import { Option } from "effect"; import { Subscription } from "foldkit"; import { DataGrid } from "@foldworks/data-grid"; import { Message } from "./message"; import type { Model } from "./model"; const grid = Subscription.lift(DataGrid.subscriptions)({ - toChildModel: (model) => model.grid, + read: (model) => Option.some(model.grid), toParentMessage: (message) => Message.GotGridMessage({ message }), }); diff --git a/apps/demo/src/workflow/subscriptions.ts b/apps/demo/src/workflow/subscriptions.ts index 00600d7..cc70b18 100644 --- a/apps/demo/src/workflow/subscriptions.ts +++ b/apps/demo/src/workflow/subscriptions.ts @@ -1,3 +1,4 @@ +import { Option } from "effect"; import { Subscription } from "foldkit"; import { Workflow } from "@foldworks/workflow"; @@ -11,7 +12,7 @@ const interaction = Subscription.lift({ dragKeyboard: Workflow.subscriptions.documentKeyboard, autoScroll: Workflow.subscriptions.autoScroll, })({ - toChildModel: (model) => model.workflow, + read: (model) => Option.some(model.workflow), toParentMessage: (message) => Message.GotWorkflowMessage({ message }), }); diff --git a/apps/demo/src/workflow/update.ts b/apps/demo/src/workflow/update.ts index 384dce5..3e8c055 100644 --- a/apps/demo/src/workflow/update.ts +++ b/apps/demo/src/workflow/update.ts @@ -1,6 +1,6 @@ import { Effect, Match as M, Option, Schema as S } from "effect"; import { Command, File, Update } from "foldkit"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { Dialog } from "@foldkit/ui"; import { History } from "@foldworks/history"; @@ -29,20 +29,21 @@ import { nodeTypes } from "./node-types"; type UpdateReturn = Update.Return; type ChildUpdateReturn = Update.ReturnWithOutMessage; -type ImportResult = Extract; +type ImportResult = Extract< + Message, + { + readonly _tag: "CompletedImportDocument" | "CancelledImportDocument" | "FailedImportDocument"; + } +>; const ExportDocument = Command.define("ExportWorkflowDocument", { args: { filename: S.String, json: S.String }, messages: [Message.CompletedExportDocument], - execute: ({ filename, json }) => Effect.sync(() => { - downloadJson(filename, json); - return Message.CompletedExportDocument(); - }), + execute: ({ filename, json }) => + Effect.sync(() => { + downloadJson(filename, json); + return Message.CompletedExportDocument(); + }), }); const ImportDocument = Command.define("ImportWorkflowDocument", { @@ -52,17 +53,22 @@ const ImportDocument = Command.define("ImportWorkflowDocument", { Message.CancelledImportDocument, Message.FailedImportDocument, ], - execute: () => File.select(["application/json", ".json"]).pipe( - Effect.flatMap((selected): Effect.Effect => { - if (Option.isNone(selected)) return Effect.succeed(Message.CancelledImportDocument()); - return File.readAsText(selected.value).pipe( - Effect.map((json): ImportResult => Message.CompletedImportDocument({ json })), - Effect.catch(() => Effect.succeed(Message.FailedImportDocument({ - reason: "The selected file could not be read.", - }))), - ); - }), - ), + execute: () => + File.select(["application/json", ".json"]).pipe( + Effect.flatMap((selected): Effect.Effect => { + if (Option.isNone(selected)) return Effect.succeed(Message.CancelledImportDocument()); + return File.readAsText(selected.value).pipe( + Effect.map((json): ImportResult => Message.CompletedImportDocument({ json })), + Effect.catch(() => + Effect.succeed( + Message.FailedImportDocument({ + reason: "The selected file could not be read.", + }), + ), + ), + ); + }), + ), }); const foldInspectorOutMessage = M.type().pipe( @@ -70,14 +76,14 @@ const foldInspectorOutMessage = M.type().pipe( M.tagsExhaustive({ Opened: () => (model) => ({ model }), Closed: () => (model) => ({ - model: evo(model, { selectedNodeId: () => Option.none() }), + model: modifyFields(model, { selectedNodeId: () => Option.none() }), }), }), ); const readInspector = (model: Model) => Option.some(model.inspector); const writeInspector = (model: Model, inspector: Dialog.Model): Model => - evo(model, { inspector: () => inspector }); + modifyFields(model, { inspector: () => inspector }); const toInspectorMessage = (message: Dialog.Message): Message => Message.GotInspectorMessage({ message }); @@ -105,110 +111,117 @@ const foldInspectorClose = Update.foldChildStep({ foldOutMessage: foldInspectorOutMessage, }); -const commitDrop = ( - previousModel: Model, - outMessage: Workflow.OutMessage, -): Update.Step => - (model) => Workflow.OutMessage.match(outMessage, { - Cancelled: () => ({ - model: evo(model, { - announcement: () => "Drag cancelled. The workflow was not changed.", +const commitDrop = + (previousModel: Model, outMessage: Workflow.OutMessage): Update.Step => + (model) => + Workflow.OutMessage.match(outMessage, { + Cancelled: () => ({ + model: modifyFields(model, { + announcement: () => "Drag cancelled. The workflow was not changed.", + }), }), - }), - Reordered: (reordered) => { - const result = applyReorder({ - document: model.document, - reordered: Workflow.OutMessage.Reordered(reordered), - operations, - createFromPalette: (type) => - isNodeKind(type) && nodeTypes[type].palette !== undefined - ? nodeTypes[type].create(`node-${model.nextId}`) - : undefined, - }); - if (result === undefined) { - const node = findNode(previousModel.document, reordered.itemId); - return { - model: evo(model, { - announcement: () => node === undefined - ? "Choose a highlighted insertion point." - : `${node.data.title} cannot be moved to that location.`, - }), - }; - } - if (result._tag === "Inserted") { + Reordered: (reordered) => { + const result = applyReorder({ + document: model.document, + reordered: Workflow.OutMessage.Reordered(reordered), + operations, + createFromPalette: (type) => + isNodeKind(type) && nodeTypes[type].palette !== undefined + ? nodeTypes[type].create(`node-${model.nextId}`) + : undefined, + }); + if (result === undefined) { + const node = findNode(previousModel.document, reordered.itemId); + return { + model: modifyFields(model, { + announcement: () => + node === undefined + ? "Choose a highlighted insertion point." + : `${node.data.title} cannot be moved to that location.`, + }), + }; + } + if (result._tag === "Inserted") { + return { + model: modifyFields(model, { + document: () => result.document, + nextId: (value) => value + 1, + revision: (value) => value + 1, + announcement: () => `${result.element.data.title} added to the workflow.`, + }), + }; + } + const movedNode = findNode(model.document, result.elementId); return { - model: evo(model, { + model: modifyFields(model, { document: () => result.document, - nextId: (value) => value + 1, revision: (value) => value + 1, - announcement: () => `${result.element.data.title} added to the workflow.`, + announcement: () => `${movedNode?.data.title ?? "Node"} moved.`, }), }; - } - const movedNode = findNode(model.document, result.elementId); - return { - model: evo(model, { - document: () => result.document, - revision: (value) => value + 1, - announcement: () => `${movedNode?.data.title ?? "Node"} moved.`, - }), - }; - }, - }); + }, + }); -const foldWorkflow = (previousModel: Model) => Update.foldChild({ - update: Workflow.update, - read: (model: Model) => Option.some(model.workflow), - write: (model, workflow) => evo(model, { workflow: () => workflow }), - toParentMessage: (message) => Message.GotWorkflowMessage({ message }), - foldOutMessage: (outMessage) => commitDrop(previousModel, outMessage), -}); +const foldWorkflow = (previousModel: Model) => + Update.foldChild({ + update: Workflow.update, + read: (model: Model) => Option.some(model.workflow), + write: (model, workflow) => modifyFields(model, { workflow: () => workflow }), + toParentMessage: (message) => Message.GotWorkflowMessage({ message }), + foldOutMessage: (outMessage) => commitDrop(previousModel, outMessage), + }); const updateSelectedNode = ( model: Model, change: Parameters[2], shouldRebalance = false, -): UpdateReturn => Option.match(model.selectedNodeId, { - onNone: () => ({ model }), - onSome: (nodeId) => { - const document = updateNode(model.document, nodeId, change); - return document === undefined - ? { model } - : { - model: evo(model, { - document: () => document, - revision: (value) => shouldRebalance ? value + 1 : value, - }), - }; - }, -}); +): UpdateReturn => + Option.match(model.selectedNodeId, { + onNone: () => ({ model }), + onSome: (nodeId) => { + const document = updateNode(model.document, nodeId, change); + return document === undefined + ? { model } + : { + model: modifyFields(model, { + document: () => document, + revision: (value) => (shouldRebalance ? value + 1 : value), + }), + }; + }, + }); const coalescingKey = (model: Model, message: Message): string | undefined => { const nodeId = Option.getOrUndefined(model.selectedNodeId); if (nodeId === undefined) return undefined; switch (message._tag) { - case "ChangedSelectedNodeTitle": return `workflow:${nodeId}:title`; - case "ChangedSelectedNodeDescription": return `workflow:${nodeId}:description`; - default: return undefined; + case "ChangedSelectedNodeTitle": + return `workflow:${nodeId}:title`; + case "ChangedSelectedNodeDescription": + return `workflow:${nodeId}:description`; + default: + return undefined; } }; const restoreFromHistory = (model: Model, direction: "Undo" | "Redo"): UpdateReturn => { - const step = direction === "Undo" - ? History.undo(model.workflowHistory, model.document) - : History.redo(model.workflowHistory, model.document); + const step = + direction === "Undo" + ? History.undo(model.workflowHistory, model.document) + : History.redo(model.workflowHistory, model.document); if (step === undefined) return { model }; const selectedNodeId = Option.getOrUndefined(model.selectedNodeId); - const keepsSelection = selectedNodeId !== undefined && - findNode(step.value, selectedNodeId) !== undefined; + const keepsSelection = + selectedNodeId !== undefined && findNode(step.value, selectedNodeId) !== undefined; return { - model: evo(model, { + model: modifyFields(model, { document: () => step.value, workflowHistory: () => step.history, - selectedNodeId: () => keepsSelection ? model.selectedNodeId : Option.none(), - inspector: () => keepsSelection - ? model.inspector - : Dialog.init({ id: model.inspector.id, isAnimated: true }), + selectedNodeId: () => (keepsSelection ? model.selectedNodeId : Option.none()), + inspector: () => + keepsSelection + ? model.inspector + : Dialog.init({ id: model.inspector.id, isAnimated: true }), nextId: () => nextWorkflowId(step.value), revision: (value) => value + 1, announcement: () => `${direction} completed for the workflow.`, @@ -219,38 +232,41 @@ const restoreFromHistory = (model: Model, direction: "Undo" | "Redo"): UpdateRet const updateCore = (model: Model, message: Message): UpdateReturn => Message.match(message, { CompletedExportDocument: () => ({ - model: evo(model, { announcement: () => "Workflow JSON exported." }), + model: modifyFields(model, { announcement: () => "Workflow JSON exported." }), }), CancelledImportDocument: () => ({ model }), FailedImportDocument: ({ reason }) => ({ - model: evo(model, { announcement: () => reason }), + model: modifyFields(model, { announcement: () => reason }), }), ClickedUndo: () => restoreFromHistory(model, "Undo"), ClickedRedo: () => restoreFromHistory(model, "Redo"), ClickedExportDocument: () => ({ model, - commands: [ExportDocument({ - filename: "candidate-workflow.workflow.json", - json: serializeWorkflowExport(model.document), - })], + commands: [ + ExportDocument({ + filename: "candidate-workflow.workflow.json", + json: serializeWorkflowExport(model.document), + }), + ], }), ClickedImportDocument: () => ({ model, commands: [ImportDocument({})] }), CompletedImportDocument: ({ json }) => { const document = parseWorkflowExport(json); return document === undefined ? { - model: evo(model, { + model: modifyFields(model, { announcement: () => "That file is not a valid workflow export.", }), } : { - model: evo(model, { + model: modifyFields(model, { document: () => document, - workflow: (workflow) => Workflow.init({ - id: workflow.id, - orientation: workflow.orientation, - activationThreshold: workflow.activationThreshold, - }), + workflow: (workflow) => + Workflow.init({ + id: workflow.id, + orientation: workflow.orientation, + activationThreshold: workflow.activationThreshold, + }), selectedNodeId: () => Option.none(), inspector: () => Dialog.init({ id: model.inspector.id, isAnimated: true }), nextId: () => nextWorkflowId(document), @@ -262,10 +278,9 @@ const updateCore = (model: Model, message: Message): UpdateReturn => GotWorkflowMessage: ({ message: workflowMessage }) => foldWorkflow(model)(model, workflowMessage), SelectedOrientation: () => ({ model }), - GotInspectorMessage: ({ message: inspectorMessage }) => - foldInspector(model, inspectorMessage), + GotInspectorMessage: ({ message: inspectorMessage }) => foldInspector(model, inspectorMessage), ClickedNode: ({ nodeId }) => { - const selected = evo(model, { + const selected = modifyFields(model, { selectedNodeId: () => Option.some(nodeId), workflowHistory: (history) => History.breakCoalescing(history), }); @@ -276,52 +291,65 @@ const updateCore = (model: Model, message: Message): UpdateReturn => if (location === undefined) return { model }; const inserted = insertNewNode(model.document, location, "action", model.nextId); if (inserted === undefined) return { model }; - return foldInspectorOpen(evo(model, { - document: () => inserted.document, - selectedNodeId: () => Option.some(inserted.node.id), - nextId: (value) => value + 1, - revision: (value) => value + 1, - announcement: () => `${inserted.node.data.title} added.`, - })); + return foldInspectorOpen( + modifyFields(model, { + document: () => inserted.document, + selectedNodeId: () => Option.some(inserted.node.id), + nextId: (value) => value + 1, + revision: (value) => value + 1, + announcement: () => `${inserted.node.data.title} added.`, + }), + ); }, - ChangedSelectedNodeTitle: ({ value }) => updateSelectedNode(model, (node) => ({ - ...node, - data: { ...node.data, title: value }, - })), - ChangedSelectedNodeDescription: ({ value }) => updateSelectedNode(model, (node) => ({ - ...node, - data: { ...node.data, description: value }, - })), - ChangedSelectedNodeType: ({ value }) => isNodeKind(value) - ? updateSelectedNode(model, (node) => ({ ...node, type: value }), true) - : { model }, - ChangedSelectedNodeSize: ({ value }) => isNodeSize(value) - ? updateSelectedNode(model, (node) => ({ - ...node, - data: { ...node.data, size: value }, - }), true) - : { model }, - ClickedDeleteSelectedNode: () => Option.match(model.selectedNodeId, { - onNone: () => ({ model }), - onSome: (nodeId) => { - const selected = findNode(model.document, nodeId); - const document = deleteNode(model.document, nodeId); - if (document === undefined) { - return { - model: evo(model, { - announcement: () => "This structural node cannot be deleted.", + ChangedSelectedNodeTitle: ({ value }) => + updateSelectedNode(model, (node) => ({ + ...node, + data: { ...node.data, title: value }, + })), + ChangedSelectedNodeDescription: ({ value }) => + updateSelectedNode(model, (node) => ({ + ...node, + data: { ...node.data, description: value }, + })), + ChangedSelectedNodeType: ({ value }) => + isNodeKind(value) + ? updateSelectedNode(model, (node) => ({ ...node, type: value }), true) + : { model }, + ChangedSelectedNodeSize: ({ value }) => + isNodeSize(value) + ? updateSelectedNode( + model, + (node) => ({ + ...node, + data: { ...node.data, size: value }, }), - }; - } - return foldInspectorClose(evo(model, { - document: () => document, - revision: (value) => value + 1, - announcement: () => `${selected?.data.title ?? "Node"} deleted.`, - })); - }, - }), + true, + ) + : { model }, + ClickedDeleteSelectedNode: () => + Option.match(model.selectedNodeId, { + onNone: () => ({ model }), + onSome: (nodeId) => { + const selected = findNode(model.document, nodeId); + const document = deleteNode(model.document, nodeId); + if (document === undefined) { + return { + model: modifyFields(model, { + announcement: () => "This structural node cannot be deleted.", + }), + }; + } + return foldInspectorClose( + modifyFields(model, { + document: () => document, + revision: (value) => value + 1, + announcement: () => `${selected?.data.title ?? "Node"} deleted.`, + }), + ); + }, + }), ClickedReset: () => { - const reset = evo(model, { + const reset = modifyFields(model, { document: () => initialDocument, nextId: () => initialModel.nextId, revision: (value) => value + 1, @@ -331,15 +359,19 @@ const updateCore = (model: Model, message: Message): UpdateReturn => }, }); -export const setOrientation = (model: Model, orientation: Model["workflow"]["orientation"]): Model => +export const setOrientation = ( + model: Model, + orientation: Model["workflow"]["orientation"], +): Model => model.workflow.orientation === orientation ? model - : evo(model, { - workflow: (workflow) => Workflow.init({ - id: workflow.id, - orientation, - activationThreshold: workflow.activationThreshold, - }), + : modifyFields(model, { + workflow: (workflow) => + Workflow.init({ + id: workflow.id, + orientation, + activationThreshold: workflow.activationThreshold, + }), workflowHistory: (history) => History.breakCoalescing(history), revision: (value) => value + 1, announcement: () => `${orientation} workflow layout selected.`, @@ -359,14 +391,15 @@ export const update = (model: Model, message: Message): ChildUpdateReturn => { const key = coalescingKey(model, message); return { ...result, - model: evo(result.model, { - workflowHistory: () => traversedHistory - ? result.model.workflowHistory - : History.record( - model.workflowHistory, - model.document, - key === undefined ? {} : { coalescingKey: key }, - ), + model: modifyFields(result.model, { + workflowHistory: () => + traversedHistory + ? result.model.workflowHistory + : History.record( + model.workflowHistory, + model.document, + key === undefined ? {} : { coalescingKey: key }, + ), }), outMessage: OutMessage.Changed(), }; diff --git a/apps/demo/vitest.e2e.config.ts b/apps/demo/vitest.e2e.config.ts index 1e4cebf..2d41468 100644 --- a/apps/demo/vitest.e2e.config.ts +++ b/apps/demo/vitest.e2e.config.ts @@ -2,6 +2,8 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { + // Vitest 5 counts pending Playwright reads within the polling deadline. + expect: { poll: { timeout: 5_000 } }, hookTimeout: 30_000, include: ["e2e/**/*.test.ts"], testTimeout: 30_000, diff --git a/package.json b/package.json index 89f2f4b..9bfe74f 100644 --- a/package.json +++ b/package.json @@ -35,24 +35,26 @@ "release:ready": "node scripts/check-release-ready.mjs", "test": "pnpm run build:packages && pnpm --recursive --if-present test", "typecheck": "pnpm run build:packages && pnpm run typecheck:infra && pnpm --recursive --if-present typecheck", - "typecheck:infra": "tsc --noEmit", + "typecheck:infra": "tsc --noEmit && vitest run --config vitest.infra.config.ts", "test:e2e": "pnpm run build:packages && pnpm --filter @foldworks/demo test:e2e", "test:visual": "pnpm run build:packages && pnpm --filter @foldworks/demo test:visual", "test:visual:update": "pnpm run build:packages && pnpm --filter @foldworks/demo test:visual:update", "version-packages": "changeset version" }, "devDependencies": { - "@alchemy.run/frontend-frameworks": "2.0.0-beta.76", + "@alchemy.run/frontend-frameworks": "2.0.0-beta.80", "@changesets/cli": "^3.0.2", - "@effect/platform-node": "4.0.0-rc.112", + "@effect/platform-node": "catalog:", + "@effect/vitest": "4.0.3", "@types/node": "catalog:", - "alchemy": "2.0.0-beta.76", + "alchemy": "2.0.0-beta.80", "effect": "catalog:", "hono": "4.12.14", "oxfmt": "^0.44.0", "oxlint": "^1.59.0", "tsup": "^8.5.1", "turbo": "^2.11.3", - "typescript": "catalog:" + "typescript": "catalog:", + "vitest": "catalog:" } } diff --git a/packages/agent/src/update.ts b/packages/agent/src/update.ts index 6ecb0db..14f38ca 100644 --- a/packages/agent/src/update.ts +++ b/packages/agent/src/update.ts @@ -20,87 +20,104 @@ import type { EventEnvelope, StreamEvent } from "./protocol"; const MeasureTranscript = Command.define("MeasureAgentTranscript", { args: { id: S.String }, messages: [Message.CompletedMeasureTranscript], - execute: ({ id }) => Effect.promise(() => new Promise>((resolve) => { - requestAnimationFrame(() => { - const element = document.getElementById(id); - if (element === null) { - resolve(Message.CompletedMeasureTranscript({ - isFollowing: true, - currentTurnId: "", - visibleTurnIds: [], - })); - return; - } - const viewport = element.getBoundingClientRect(); - const turns = Array.from(element.querySelectorAll("[data-agent-turn-id]")); - const visibleTurnIds = turns - .filter((turn) => { - const bounds = turn.getBoundingClientRect(); - return bounds.bottom > viewport.top && bounds.top < viewport.bottom; - }) - .map((turn) => turn.dataset.agentTurnId ?? "") - .filter(Boolean); - const anchors = turns.filter((turn) => turn.dataset.agentScrollAnchor === "true"); - const currentAnchor = [...anchors].reverse().find((turn) => - turn.getBoundingClientRect().top <= viewport.top + 72); - resolve(Message.CompletedMeasureTranscript({ - isFollowing: element.scrollHeight - element.clientHeight - element.scrollTop < 72, - currentTurnId: currentAnchor?.dataset.agentTurnId ?? visibleTurnIds[0] ?? "", - visibleTurnIds, - })); - }); - })), + execute: ({ id }) => + Effect.promise( + () => + new Promise>((resolve) => { + requestAnimationFrame(() => { + const element = document.getElementById(id); + if (element === null) { + resolve( + Message.CompletedMeasureTranscript({ + isFollowing: true, + currentTurnId: "", + visibleTurnIds: [], + }), + ); + return; + } + const viewport = element.getBoundingClientRect(); + const turns = Array.from(element.querySelectorAll("[data-agent-turn-id]")); + const visibleTurnIds = turns + .filter((turn) => { + const bounds = turn.getBoundingClientRect(); + return bounds.bottom > viewport.top && bounds.top < viewport.bottom; + }) + .map((turn) => turn.dataset.agentTurnId ?? "") + .filter(Boolean); + const anchors = turns.filter((turn) => turn.dataset.agentScrollAnchor === "true"); + const currentAnchor = [...anchors] + .reverse() + .find((turn) => turn.getBoundingClientRect().top <= viewport.top + 72); + resolve( + Message.CompletedMeasureTranscript({ + isFollowing: element.scrollHeight - element.clientHeight - element.scrollTop < 72, + currentTurnId: currentAnchor?.dataset.agentTurnId ?? visibleTurnIds[0] ?? "", + visibleTurnIds, + }), + ); + }); + }), + ), }); const ScrollLatest = Command.define("ScrollAgentTranscriptLatest", { args: { id: S.String }, messages: [Message.CompletedScrollLatest], - execute: ({ id }) => Effect.promise(() => new Promise>((resolve) => { - requestAnimationFrame(() => { - const element = document.getElementById(id); - if (element !== null) element.scrollTop = element.scrollHeight; - resolve(Message.CompletedScrollLatest()); - }); - })), + execute: ({ id }) => + Effect.promise( + () => + new Promise>((resolve) => { + requestAnimationFrame(() => { + const element = document.getElementById(id); + if (element !== null) element.scrollTop = element.scrollHeight; + resolve(Message.CompletedScrollLatest()); + }); + }), + ), }); const ScrollToTurn = Command.define("ScrollAgentTranscriptToTurn", { args: { id: S.String, turnId: S.String, previousItemPeek: S.Number }, messages: [Message.CompletedScrollToTurn], - execute: ({ id, turnId, previousItemPeek }) => Effect.promise(() => - new Promise>((resolve) => { - requestAnimationFrame(() => { - const element = document.getElementById(id); - const turn = element === null - ? undefined - : Array.from(element.querySelectorAll("[data-agent-turn-id]")) - .find((candidate) => candidate.dataset.agentTurnId === turnId); - if (element !== null && turn !== undefined) { - const viewport = element.getBoundingClientRect(); - const bounds = turn.getBoundingClientRect(); - element.scrollTop += bounds.top - viewport.top - Math.max(0, previousItemPeek); - } - resolve(Message.CompletedScrollToTurn({ turnId })); - }); - }), - ), + execute: ({ id, turnId, previousItemPeek }) => + Effect.promise( + () => + new Promise>((resolve) => { + requestAnimationFrame(() => { + const element = document.getElementById(id); + const turn = + element === null + ? undefined + : Array.from(element.querySelectorAll("[data-agent-turn-id]")).find( + (candidate) => candidate.dataset.agentTurnId === turnId, + ); + if (element !== null && turn !== undefined) { + const viewport = element.getBoundingClientRect(); + const bounds = turn.getBoundingClientRect(); + element.scrollTop += bounds.top - viewport.top - Math.max(0, previousItemPeek); + } + resolve(Message.CompletedScrollToTurn({ turnId })); + }); + }), + ), }); const CopyTurn = Command.define("CopyAgentTurn", { args: { turnId: S.String, text: S.String }, messages: [Message.CompletedCopyTurn], - execute: ({ turnId, text }) => Effect.promise(async () => { - await navigator.clipboard.writeText(text); - return Message.CompletedCopyTurn({ turnId }); - }), + execute: ({ turnId, text }) => + Effect.promise(async () => { + await navigator.clipboard.writeText(text); + return Message.CompletedCopyTurn({ turnId }); + }), }); const ClearCopiedTurn = Command.define("ClearCopiedAgentTurn", { args: { turnId: S.String }, messages: [Message.ClearedCopiedTurn], - execute: ({ turnId }) => Effect.sleep("2 seconds").pipe( - Effect.as(Message.ClearedCopiedTurn({ turnId })), - ), + execute: ({ turnId }) => + Effect.sleep("2 seconds").pipe(Effect.as(Message.ClearedCopiedTurn({ turnId }))), }); const mapParts = ( @@ -109,9 +126,11 @@ const mapParts = ( transform: (part: ConversationPart) => ConversationPart, ): Model => ({ ...model, - transcript: model.transcript.map((turn) => turn.role !== "Assistant" - ? turn - : { ...turn, parts: turn.parts.map((part) => predicate(part) ? transform(part) : part) }), + transcript: model.transcript.map((turn) => + turn.role !== "Assistant" + ? turn + : { ...turn, parts: turn.parts.map((part) => (predicate(part) ? transform(part) : part)) }, + ), }); const appendAssistantPart = (model: Model, part: ConversationPart): Model => { @@ -125,9 +144,9 @@ const appendAssistantPart = (model: Model, part: ConversationPart): Model => { if (index < 0) return model; return { ...model, - transcript: model.transcript.map((turn, turnIndex) => turnIndex === index - ? { ...turn, parts: [...turn.parts, part] } - : turn), + transcript: model.transcript.map((turn, turnIndex) => + turnIndex === index ? { ...turn, parts: [...turn.parts, part] } : turn, + ), }; }; @@ -135,31 +154,34 @@ const updateText = ( model: Model, partId: string, transform: (part: Extract) => ConversationPart, -): Model => mapParts( - model, - (part) => part._tag === "Text" && part.id === partId, - (part) => transform(part as Extract), -); +): Model => + mapParts( + model, + (part) => part._tag === "Text" && part.id === partId, + (part) => transform(part as Extract), + ); const updateReasoning = ( model: Model, partId: string, transform: (part: ReasoningPart) => ConversationPart, -): Model => mapParts( - model, - (part) => part._tag === "Reasoning" && part.id === partId, - (part) => transform(part as ReasoningPart), -); +): Model => + mapParts( + model, + (part) => part._tag === "Reasoning" && part.id === partId, + (part) => transform(part as ReasoningPart), + ); const updateTool = ( model: Model, callId: string, transform: (part: ToolPart) => ConversationPart, -): Model => mapParts( - model, - (part) => part._tag === "Tool" && part.callId === callId, - (part) => transform(part as ToolPart), -); +): Model => + mapParts( + model, + (part) => part._tag === "Tool" && part.callId === callId, + (part) => transform(part as ToolPart), + ); const startRun = ( model: Model, @@ -172,12 +194,23 @@ const startRun = ( const runId = `${model.id}-run-${number}`; const userTurnId = `${runId}-user`; const turns: ReadonlyArray = [ - ...(appendUser ? [{ - id: userTurnId, - role: "User" as const, - modelId: "", - parts: [{ _tag: "Text" as const, id: `${runId}-prompt`, text: value, status: "Complete" as const }], - }] : []), + ...(appendUser + ? [ + { + id: userTurnId, + role: "User" as const, + modelId: "", + parts: [ + { + _tag: "Text" as const, + id: `${runId}-prompt`, + text: value, + status: "Complete" as const, + }, + ], + }, + ] + : []), { id: `${runId}-assistant`, role: "Assistant", modelId: model.selectedModel, parts: [] }, ]; return { @@ -198,15 +231,21 @@ const startRun = ( }; const applyEvent = (model: Model, event: StreamEvent, sequence: number): Model => { - const streaming = model.runState._tag === "Streaming" - ? { ...model.runState, lastSequence: sequence } - : model.runState; + const streaming = + model.runState._tag === "Streaming" + ? { ...model.runState, lastSequence: sequence } + : model.runState; let next: Model = { ...model, runState: streaming }; switch (event._tag) { case "Started": return { ...next, announcement: "Assistant is streaming a response." }; case "TextStarted": - return appendAssistantPart(next, { _tag: "Text", id: event.partId, text: "", status: "Streaming" }); + return appendAssistantPart(next, { + _tag: "Text", + id: event.partId, + text: "", + status: "Streaming", + }); case "TextDelta": return updateText(next, event.partId, (part) => ({ ...part, text: part.text + event.delta })); case "TextFinished": @@ -226,23 +265,36 @@ const applyEvent = (model: Model, event: StreamEvent, sequence: number): Model = case "ReasoningFinished": return updateReasoning(next, event.partId, (part) => ({ ...part, status: "Complete" })); case "ToolInputStarted": - return appendAssistantPart({ ...next, announcement: `${event.name} tool call prepared.` }, { - _tag: "Tool", - callId: event.callId, - name: event.name, - input: "", - output: "", - permissionReason: "", - status: "Preparing", - }); + return appendAssistantPart( + { ...next, announcement: `${event.name} tool call prepared.` }, + { + _tag: "Tool", + callId: event.callId, + name: event.name, + input: "", + output: "", + permissionReason: "", + status: "Preparing", + }, + ); case "ToolInputDelta": - return updateTool(next, event.callId, (part) => ({ ...part, input: part.input + event.delta })); + return updateTool(next, event.callId, (part) => ({ + ...part, + input: part.input + event.delta, + })); case "ToolCallReady": return { ...next, announcement: "Tool input is ready." }; case "ToolStarted": - return updateTool({ ...next, announcement: "Tool is running." }, event.callId, (part) => ({ ...part, status: "Running" })); + return updateTool({ ...next, announcement: "Tool is running." }, event.callId, (part) => ({ + ...part, + status: "Running", + })); case "ToolResult": - return updateTool({ ...next, announcement: "Tool completed." }, event.callId, (part) => ({ ...part, status: "Completed", output: event.output })); + return updateTool({ ...next, announcement: "Tool completed." }, event.callId, (part) => ({ + ...part, + status: "Completed", + output: event.output, + })); case "PermissionRequested": next = updateTool(next, event.callId, (part) => ({ ...part, @@ -264,15 +316,15 @@ const applyEvent = (model: Model, event: StreamEvent, sequence: number): Model = case "Failed": next = mapParts( next, - (part) => (part._tag === "Text" || part._tag === "Reasoning") && part.status === "Streaming", - (part) => part._tag === "Text" || part._tag === "Reasoning" - ? { ...part, status: "Failed" } - : part, + (part) => + (part._tag === "Text" || part._tag === "Reasoning") && part.status === "Streaming", + (part) => + part._tag === "Text" || part._tag === "Reasoning" ? { ...part, status: "Failed" } : part, ); next = mapParts( next, (part) => part._tag === "Tool" && ["Preparing", "Running"].includes(part.status), - (part) => part._tag === "Tool" ? { ...part, status: "Failed" } : part, + (part) => (part._tag === "Tool" ? { ...part, status: "Failed" } : part), ); return { ...next, @@ -287,7 +339,8 @@ const receive = (model: Model, envelope: EventEnvelope): Update.Return { let next = mapParts( model, (part) => (part._tag === "Text" || part._tag === "Reasoning") && part.status === "Streaming", - (part) => part._tag === "Text" || part._tag === "Reasoning" - ? { ...part, status: "Interrupted" } - : part, + (part) => + part._tag === "Text" || part._tag === "Reasoning" ? { ...part, status: "Interrupted" } : part, ); next = mapParts( next, - (part) => part._tag === "Tool" && ["Preparing", "Running", "WaitingApproval"].includes(part.status), - (part) => part._tag === "Tool" - ? { ...part, status: "Cancelled", output: part.output || "Cancelled before execution." } - : part, + (part) => + part._tag === "Tool" && ["Preparing", "Running", "WaitingApproval"].includes(part.status), + (part) => + part._tag === "Tool" + ? { ...part, status: "Cancelled", output: part.output || "Cancelled before execution." } + : part, ); return { ...next, runState: { _tag: "Idle" }, announcement: "Assistant response stopped." }; }; @@ -322,21 +376,23 @@ const foldModelPicker = Update.foldChild({ read: (model: Model) => Option.some(model.modelPicker), write: (model, modelPicker) => ({ ...model, modelPicker }), toParentMessage: (message) => Message.GotModelPickerMessage({ message }), - foldOutMessage: (outMessage) => (model: Model) => isActive(model) - ? { model } - : { - model: { - ...model, - selectedModel: outMessage.value, - announcement: `${outMessage.value} selected.`, + foldOutMessage: (outMessage) => (model: Model) => + isActive(model) + ? { model } + : { + model: { + ...model, + selectedModel: outMessage.value, + announcement: `${outMessage.value} selected.`, + }, }, - }, }); export const update = (model: Model, message: Message): Update.Return => - Message.match(message, { + Message.match>(message, { GotModelPickerMessage: ({ message }) => foldModelPicker(model, message), - ChangedDraft: ({ value }) => isActive(model) ? { model } : { model: { ...model, draft: value } }, + ChangedDraft: ({ value }) => + isActive(model) ? { model } : { model: { ...model, draft: value } }, Submitted: () => startRun(model, model.draft, true), SelectedSuggestion: ({ prompt }) => startRun({ ...model, draft: prompt }, prompt, true), ReceivedStreamEvent: ({ envelope }) => receive(model, envelope), @@ -358,26 +414,39 @@ export const update = (model: Model, message: Message): Update.Return isActive(model) ? { model: interrupt(model) } : { model }, - Retried: () => model.runState._tag === "Failed" - ? startRun({ ...model, runState: { _tag: "Idle" } }, latestUserPrompt(model), false) - : { model }, + Stopped: () => (isActive(model) ? { model: interrupt(model) } : { model }), + Retried: () => + model.runState._tag === "Failed" + ? startRun({ ...model, runState: { _tag: "Idle" } }, latestUserPrompt(model), false) + : { model }, RegeneratedTurn: ({ turnId }) => { if (isActive(model)) return { model }; - const index = model.transcript.findIndex((turn) => turn.id === turnId && turn.role === "Assistant"); - const latestAssistantIndex = model.transcript.findLastIndex((turn) => turn.role === "Assistant"); + const index = model.transcript.findIndex( + (turn) => turn.id === turnId && turn.role === "Assistant", + ); + const latestAssistantIndex = model.transcript.findLastIndex( + (turn) => turn.role === "Assistant", + ); if (index < 0 || index !== latestAssistantIndex) return { model }; - const prompt = [...model.transcript.slice(0, index)].reverse() - .find((turn) => turn.role === "User")?.parts - .find((part) => part._tag === "Text")?.text ?? ""; - return startRun({ - ...model, - transcript: model.transcript.filter((_, turnIndex) => turnIndex !== index), - }, prompt, false); + const prompt = + [...model.transcript.slice(0, index)] + .reverse() + .find((turn) => turn.role === "User") + ?.parts.find((part) => part._tag === "Text")?.text ?? ""; + return startRun( + { + ...model, + transcript: model.transcript.filter((_, turnIndex) => turnIndex !== index), + }, + prompt, + false, + ); }, CopiedTurn: ({ turnId }) => { const turn = model.transcript.find((candidate) => candidate.id === turnId); @@ -408,16 +477,19 @@ export const update = (model: Model, message: Message): Update.Return model.transcript.some((turn) => turn.id === turnId) - ? { - model: { ...model, isFollowing: false, anchoredTurnId: turnId }, - commands: [ScrollToTurn({ - id: transcriptId(model), - turnId, - previousItemPeek: 64, - })], - } - : { model }, + JumpedToTurn: ({ turnId }) => + model.transcript.some((turn) => turn.id === turnId) + ? { + model: { ...model, isFollowing: false, anchoredTurnId: turnId }, + commands: [ + ScrollToTurn({ + id: transcriptId(model), + turnId, + previousItemPeek: 64, + }), + ], + } + : { model }, CompletedScrollLatest: () => ({ model }), CompletedScrollToTurn: ({ turnId }) => ({ model: { ...model, currentTurnId: turnId }, diff --git a/packages/code-editor/src/native/view.ts b/packages/code-editor/src/native/view.ts index 31b21ce..882ff2e 100644 --- a/packages/code-editor/src/native/view.ts +++ b/packages/code-editor/src/native/view.ts @@ -357,38 +357,35 @@ export const view = ( }), ], ), - h.textarea( - [ - h.Id(model.id), - h.Class("native-editor__input"), - h.AriaLabel(label), - h.Attribute( - "aria-describedby", - `${model.id}-help${hoverRow === undefined ? "" : ` ${model.id}-hover`}`, - ), - h.Attribute("spellcheck", "false"), - h.Attribute("autocapitalize", "off"), - h.Attribute("autocomplete", "off"), - h.Wrap(model.options.lineWrapping ? "soft" : "off"), - h.Readonly(model.options.readOnly), - { _tag: "Prop", key: "foldkitNative", value: model }, - ...(showsCompletion - ? [ - h.AriaAutocomplete("list"), - h.Attribute("aria-controls", `${model.id}-completions`), - h.Attribute( - "aria-activedescendant", - optionId( - `${model.id}-completions`, - Math.min(model.completion!.index, suggestions.length - 1), - ), + h.textarea([ + h.Id(model.id), + h.Class("native-editor__input"), + h.AriaLabel(label), + h.Attribute( + "aria-describedby", + `${model.id}-help${hoverRow === undefined ? "" : ` ${model.id}-hover`}`, + ), + h.Attribute("spellcheck", "false"), + h.Attribute("autocapitalize", "off"), + h.Attribute("autocomplete", "off"), + h.Wrap(model.options.lineWrapping ? "soft" : "off"), + h.Readonly(model.options.readOnly), + { _tag: "Prop", key: "foldkitNative", value: model }, + ...(showsCompletion + ? [ + h.AriaAutocomplete("list"), + h.Attribute("aria-controls", `${model.id}-completions`), + h.Attribute( + "aria-activedescendant", + optionId( + `${model.id}-completions`, + Math.min(model.completion!.index, suggestions.length - 1), ), - ] - : []), - h.OnMount(Mount.mapMessage(ObserveInput(), toParentMessage)), - ], - [], - ), + ), + ] + : []), + h.OnMount(Mount.mapMessage(ObserveInput(), toParentMessage)), + ]), ], ), ], diff --git a/packages/editor/src/browser.ts b/packages/editor/src/browser.ts index 0ff7da6..8b26fd6 100644 --- a/packages/editor/src/browser.ts +++ b/packages/editor/src/browser.ts @@ -1,6 +1,7 @@ import { segments, offsetAtPoint, optionId, mostSevere } from "@foldworks/text-intelligence"; import type { TextIntelligence } from "./intelligence"; import type { Html, HtmlBuilder } from "foldkit/html"; +import { Option, Schema } from "effect"; import { collapsed, find, @@ -1018,7 +1019,8 @@ export const contentView = ( { _tag: "OnCustomEvent", name: EVENT, - f: (event: CustomEvent) => event.detail, + f: (event: CustomEvent): Option.Option => + Schema.is(Message)(event.detail) ? Option.some(event.detail) : Option.none(), }, ], [], diff --git a/packages/editor/src/node-view.ts b/packages/editor/src/node-view.ts index 52b51c4..7b9057d 100644 --- a/packages/editor/src/node-view.ts +++ b/packages/editor/src/node-view.ts @@ -58,9 +58,7 @@ export const mountBlockView = ( toParentMessage: (message) => Event.Action({ message }), }), subscriptions: Subscription.make()(() => ({ - node: Port.subscription(ports.inbound.node, (node) => - Event.Changed({ node }), - ), + node: Port.subscriptionEntry(ports.inbound.node, (node) => Event.Changed({ node })), })), }), ); diff --git a/packages/generative-ui/README.md b/packages/generative-ui/README.md index ad349e2..1a6b1da 100644 --- a/packages/generative-ui/README.md +++ b/packages/generative-ui/README.md @@ -55,7 +55,7 @@ fan-out, and string sizes. ## Generate with Effect AI `generate` uses the provider-neutral `LanguageModel` service from -`effect/unstable/ai`. The same Effect Schema sent to the model decodes its +`effect/ai`. The same Effect Schema sent to the model decodes its structured output. ```ts diff --git a/packages/generative-ui/src/ai.ts b/packages/generative-ui/src/ai.ts index 02b557c..3b2a4d5 100644 --- a/packages/generative-ui/src/ai.ts +++ b/packages/generative-ui/src/ai.ts @@ -1,5 +1,5 @@ import { Effect } from "effect"; -import { LanguageModel } from "effect/unstable/ai"; +import { LanguageModel } from "effect/ai"; import type { Catalog } from "./catalog"; import { catalogPrompt } from "./catalog"; diff --git a/packages/generative-ui/src/generative-ui.test.ts b/packages/generative-ui/src/generative-ui.test.ts index e5bb605..7cd0f25 100644 --- a/packages/generative-ui/src/generative-ui.test.ts +++ b/packages/generative-ui/src/generative-ui.test.ts @@ -1,5 +1,5 @@ import { Context, Option, Result, Schema as S } from "effect"; -import { Tool } from "effect/unstable/ai"; +import { Tool } from "effect/ai"; import { inertHtml as h, type Html } from "foldkit/html"; import { Scene } from "foldkit/test"; import { describe, expect, it } from "vitest"; diff --git a/packages/generative-ui/src/mcp.ts b/packages/generative-ui/src/mcp.ts index 5804de3..db04535 100644 --- a/packages/generative-ui/src/mcp.ts +++ b/packages/generative-ui/src/mcp.ts @@ -1,5 +1,5 @@ import { Effect, Schema as S } from "effect"; -import { McpServer, Tool, Toolkit } from "effect/unstable/ai"; +import { McpServer, Tool, Toolkit } from "effect/ai"; import type { Catalog } from "./catalog"; diff --git a/packages/outliner/src/view.ts b/packages/outliner/src/view.ts index 35f1db0..eaed004 100644 --- a/packages/outliner/src/view.ts +++ b/packages/outliner/src/view.ts @@ -382,27 +382,24 @@ const rowView = ( h.div( [h.Class("fw-outliner__cell"), h.DataAttribute("decorated", String(painted))], [ - h.textarea( - [ - h.Id(ids.text(row.id)), - h.Class("fw-outliner__text"), - h.Rows(1), - h.Value(row.text), - h.AriaLabel(`Item text, level ${row.depth + 1}`), - h.Spellcheck(inputs.spellcheck ?? true), - h.Readonly(readOnly), - h.DataAttribute("outline-text", "true"), - ...(hover?.id === row.id ? [h.AriaDescribedBy(ids.hover)] : []), - ...(suggestions?.id === row.id - ? [ - h.AriaAutocomplete("list"), - h.AriaControls(ids.completion), - h.AriaActiveDescendant(optionId(ids.completion, suggestions.index)), - ] - : []), - ], - [], - ), + h.textarea([ + h.Id(ids.text(row.id)), + h.Class("fw-outliner__text"), + h.Rows(1), + h.Value(row.text), + h.AriaLabel(`Item text, level ${row.depth + 1}`), + h.Spellcheck(inputs.spellcheck ?? true), + h.Readonly(readOnly), + h.DataAttribute("outline-text", "true"), + ...(hover?.id === row.id ? [h.AriaDescribedBy(ids.hover)] : []), + ...(suggestions?.id === row.id + ? [ + h.AriaAutocomplete("list"), + h.AriaControls(ids.completion), + h.AriaActiveDescendant(optionId(ids.completion, suggestions.index)), + ] + : []), + ]), h.div( [ h.Class("fw-outliner__mirror"), @@ -511,18 +508,15 @@ const placeholderView = ( h.div( [h.Class("fw-outliner__cell")], [ - h.textarea( - [ - h.Class("fw-outliner__text"), - h.Rows(1), - h.Value(""), - h.Placeholder(placeholder.label), - h.AriaLabel(name), - h.Spellcheck(inputs.spellcheck ?? true), - h.DataAttribute("placeholder-text", "true"), - ], - [], - ), + h.textarea([ + h.Class("fw-outliner__text"), + h.Rows(1), + h.Value(""), + h.Placeholder(placeholder.label), + h.AriaLabel(name), + h.Spellcheck(inputs.spellcheck ?? true), + h.DataAttribute("placeholder-text", "true"), + ]), ], ), ], diff --git a/packages/pdf-annotator/src/subscriptions.ts b/packages/pdf-annotator/src/subscriptions.ts index 722c805..e30d355 100644 --- a/packages/pdf-annotator/src/subscriptions.ts +++ b/packages/pdf-annotator/src/subscriptions.ts @@ -1,4 +1,4 @@ -import { Effect, Schema as S, Stream } from "effect"; +import { Option, Effect, Schema as S, Stream } from "effect"; import { Subscription } from "foldkit"; import { DragAndDrop } from "@foldkit/ui"; @@ -10,7 +10,7 @@ const drag = Subscription.lift({ pdfAnnotationEscape: DragAndDrop.subscriptions.documentEscape, pdfAnnotationKeyboard: DragAndDrop.subscriptions.documentKeyboard, })({ - toChildModel: (model) => model.dragAndDrop, + read: (model) => Option.some(model.dragAndDrop), toParentMessage: (message) => Message.GotDragMessage({ message }), }); @@ -21,20 +21,23 @@ const resize = Subscription.make()((entry) => ({ modelToDependencies: (model) => ({ isResizing: model.resizeState._tag === "Resizing", }), - dependenciesToStream: ({ isResizing }) => Stream.when( - Stream.merge( - Stream.fromEventListener(document, "pointermove").pipe( - Stream.map((event) => Message.MovedResize({ - screenX: event.screenX, - screenY: event.screenY, - })), - ), - Stream.fromEventListener(document, "pointerup").pipe( - Stream.map(() => Message.FinishedResize()), + dependenciesToStream: ({ isResizing }) => + Stream.when( + Stream.merge( + Stream.fromEventListener(document, "pointermove").pipe( + Stream.map((event) => + Message.MovedResize({ + screenX: event.screenX, + screenY: event.screenY, + }), + ), + ), + Stream.fromEventListener(document, "pointerup").pipe( + Stream.map(() => Message.FinishedResize()), + ), ), + Effect.sync(() => isResizing), ), - Effect.sync(() => isResizing), - ), }, ), })); diff --git a/packages/pdf-annotator/src/view.ts b/packages/pdf-annotator/src/view.ts index f7a131d..e7db073 100644 --- a/packages/pdf-annotator/src/view.ts +++ b/packages/pdf-annotator/src/view.ts @@ -424,15 +424,13 @@ const jsonInspector = (model: Model, h: HtmlBuilder): Html => ), ], ), - h.textarea( - [ - h.Class("fk-pdf-annotator__json-editor"), - h.AriaLabel("Annotation document JSON"), - h.Spellcheck(false), - h.OnInput((value) => Message.ChangedJsonDraft({ value })), - ], - [model.jsonDraft], - ), + h.textarea([ + h.Class("fk-pdf-annotator__json-editor"), + h.AriaLabel("Annotation document JSON"), + h.Value(model.jsonDraft), + h.Spellcheck(false), + h.OnInput((value) => Message.ChangedJsonDraft({ value })), + ]), model.jsonError === "" ? h.p( [h.Class("fk-pdf-annotator__json-help")], diff --git a/packages/query-builder/src/subscriptions.ts b/packages/query-builder/src/subscriptions.ts index 4f4182c..04bf45d 100644 --- a/packages/query-builder/src/subscriptions.ts +++ b/packages/query-builder/src/subscriptions.ts @@ -1,3 +1,4 @@ +import { Option } from "effect"; import { Subscription } from "foldkit"; import { DragAndDrop } from "@foldkit/ui"; @@ -5,6 +6,6 @@ import { Message } from "./message"; import type { Model } from "./model"; export const subscriptions = Subscription.lift(DragAndDrop.subscriptions)({ - toChildModel: (model) => model.interaction, + read: (model) => Option.some(model.interaction), toParentMessage: (message) => Message.GotInteractionMessage({ message }), }); diff --git a/packages/query-builder/src/update.ts b/packages/query-builder/src/update.ts index 3ab225a..6677f60 100644 --- a/packages/query-builder/src/update.ts +++ b/packages/query-builder/src/update.ts @@ -1,6 +1,6 @@ import { Option } from "effect"; import { Update } from "foldkit"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { DragAndDrop } from "@foldkit/ui"; import { applyNodeReorder } from "./interaction"; @@ -30,7 +30,7 @@ const commitDrop = const foldInteraction = Update.foldChild({ update: DragAndDrop.update, read: (model: Model) => Option.some(model.interaction), - write: (model, interaction) => evo(model, { interaction: () => interaction }), + write: (model, interaction) => modifyFields(model, { interaction: () => interaction }), toParentMessage: (message) => Message.GotInteractionMessage({ message }), foldOutMessage: commitDrop, }); diff --git a/packages/sidebar/src/update.ts b/packages/sidebar/src/update.ts index 269df63..536a3a9 100644 --- a/packages/sidebar/src/update.ts +++ b/packages/sidebar/src/update.ts @@ -1,5 +1,5 @@ import { Update } from "foldkit"; -import { evo } from "foldkit/struct"; +import { modifyFields } from "foldkit/struct"; import { Message } from "./message"; import type { Model } from "./model"; @@ -7,20 +7,20 @@ import type { Model } from "./model"; export const update = (model: Model, message: Message): Update.Return => Message.match(message, { ToggledCollapsed: () => ({ - model: evo(model, { + model: modifyFields(model, { isCollapsed: (value) => !value, - announcement: () => model.isCollapsed ? "Navigation expanded." : "Navigation collapsed.", + announcement: () => (model.isCollapsed ? "Navigation expanded." : "Navigation collapsed."), }), }), ToggledMobile: () => ({ - model: evo(model, { + model: modifyFields(model, { isMobileOpen: (value) => !value, - announcement: () => model.isMobileOpen ? "Navigation closed." : "Navigation opened.", + announcement: () => (model.isMobileOpen ? "Navigation closed." : "Navigation opened."), }), }), ClosedMobile: () => ({ model: model.isMobileOpen - ? evo(model, { + ? modifyFields(model, { isMobileOpen: () => false, announcement: () => "Navigation closed.", }) diff --git a/packages/ui/README.md b/packages/ui/README.md index 3051d21..e1c3b1f 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -33,6 +33,11 @@ compatibility. Catalog coverage does not imply behavioral parity: see the ## Setup +This release is tested with Effect `4.0.3`, Foldkit and `@foldkit/ui` +`0.168.0`, and StyleX `0.19.0`. These Foldkit releases declare exact Effect +`4.0.0` peers; using the tested `4.0.3` combination requires the scoped peer +exceptions described in [dependency compatibility](https://github.com/bjacobso/foldworks#dependency-compatibility). + Foldworks ships its StyleX expressions so applications can combine them with their own atomic styles. Configure the StyleX transform in the consuming application. For Vite: diff --git a/packages/ui/src/animation.test.ts b/packages/ui/src/animation.test.ts index b56c380..9b5060f 100644 --- a/packages/ui/src/animation.test.ts +++ b/packages/ui/src/animation.test.ts @@ -59,7 +59,10 @@ describe("styled animation", () => { Animation.Message.Hid(), ); expect(hidden.model.transitionState).toBe("LeaveStart"); - const leaving = Animation.update(hidden.model, Animation.Message.CompletedWaitForPaint()); + const leaving = Animation.update( + hidden.model, + Animation.Message.CompletedWaitForPaint({ generation: hidden.model.transitionGeneration }), + ); expect(leaving.model.transitionState).toBe("LeaveAnimating"); expect(leaving.outMessage).toBeUndefined(); expect(leaving.commands).toHaveLength(1); diff --git a/packages/ui/src/checkbox.ts b/packages/ui/src/checkbox.ts index 86400ac..66093f0 100644 --- a/packages/ui/src/checkbox.ts +++ b/packages/ui/src/checkbox.ts @@ -32,6 +32,7 @@ export const view = (config: ViewConfig, h: HtmlBuilder format({ month: "long" }, month, 1)), shortMonthNames: Array.from({ length: 12 }, (_, month) => format({ month: "short" }, month, 1)), diff --git a/packages/ui/src/editable-text.ts b/packages/ui/src/editable-text.ts index 519107f..db72b92 100644 --- a/packages/ui/src/editable-text.ts +++ b/packages/ui/src/editable-text.ts @@ -50,8 +50,7 @@ export const Focus = Command.define("FocusEditableText", { Dom.focus(`#${CSS.escape(id)}`).pipe( Effect.andThen( Effect.sync(() => { - if (select) - (document.getElementById(id) as HTMLInputElement | null)?.select(); + if (select) (document.getElementById(id) as HTMLInputElement | null)?.select(); }), ), Effect.ignore, @@ -73,8 +72,7 @@ export const update = ( policy: Policy, ): Update.ReturnWithOutMessage => { if (message._tag === "Focused") return { model }; - if (policy.isDisabled || policy.isReadOnly || policy.isPending) - return { model }; + if (policy.isDisabled || policy.isReadOnly || policy.isPending) return { model }; switch (message._tag) { case "Started": { const next: Model = { @@ -87,10 +85,7 @@ export const update = ( } case "Changed": return { - model: - model.mode === "edit" - ? { ...model, draft: message.value, error: "" } - : model, + model: model.mode === "edit" ? { ...model, draft: message.value, error: "" } : model, }; case "Blurred": return policy.blur === "keep" @@ -170,21 +165,16 @@ export const control = ( ...(config.onBlur === undefined ? [] : [h.OnBlur(config.onBlur)]), h.OnKeyDownPreventDefault((key, mods) => { if (blocked) return Option.none(); - if (key === "Escape" && config.escape !== "keep") - return Option.some(config.onCancel); - const policy = - config.enter ?? (config.multiline ? "mod-enter" : "commit"); + if (key === "Escape" && config.escape !== "keep") return Option.some(config.onCancel); + const policy = config.enter ?? (config.multiline ? "mod-enter" : "commit"); return key === "Enter" && - (policy === "commit" || - (policy === "mod-enter" && (mods.metaKey || mods.ctrlKey))) + (policy === "commit" || (policy === "mod-enter" && (mods.metaKey || mods.ctrlKey))) ? Option.some(config.onCommit) : Option.none(); }), ]; return h.div(sxAttrs(h, styles.column), [ - config.multiline - ? h.textarea([...attrs, h.Rows(3)], []) - : h.input([...attrs, h.Type("text")]), + config.multiline ? h.textarea([...attrs, h.Rows(3)]) : h.input([...attrs, h.Type("text")]), h.span( [ h.Id(`${config.id}-status`), @@ -223,9 +213,7 @@ export const view = ( h.AriaBusy(config.isPending === true), h.AriaLabel(`Edit ${config.label}`), h.Disabled( - config.isDisabled === true || - config.isReadOnly === true || - config.isPending === true, + config.isDisabled === true || config.isReadOnly === true || config.isPending === true, ), h.OnClick(send(Message.Started())), ...sxAttrs(h, styles.button), @@ -233,12 +221,7 @@ export const view = ( [config.value || "Click to edit"], ), ...(config.isPending - ? [ - h.span( - [h.Role("status"), ...sxAttrs(h, styles.muted)], - ["Saving…"], - ), - ] + ? [h.span([h.Role("status"), ...sxAttrs(h, styles.muted)], ["Saving…"])] : []), ]) : control( diff --git a/packages/ui/src/field.ts b/packages/ui/src/field.ts index 833b19e..5dab79a 100644 --- a/packages/ui/src/field.ts +++ b/packages/ui/src/field.ts @@ -245,6 +245,7 @@ export const input = (config: InputConfig, h: HtmlBuilder h.div(rootAttrs(config, h, fieldStyles.root), [ @@ -343,6 +344,7 @@ export const textarea = ( ...(config.onInput === undefined ? {} : { onInput: config.onInput }), ...(config.isDisabled === undefined ? {} : { isDisabled: config.isDisabled }), ...(config.isReadOnly === undefined ? {} : { isReadOnly: config.isReadOnly }), + hasDescription: true, isInvalid: config.error !== undefined, toView: (attributes) => h.div(rootAttrs(config, h, fieldStyles.root), [ @@ -396,6 +398,7 @@ export const select = (config: SelectConfig, h: HtmlBuilder h.div(rootAttrs(config, h, fieldStyles.root), [ diff --git a/packages/ui/src/fieldset.ts b/packages/ui/src/fieldset.ts index 817af4b..9288e5c 100644 --- a/packages/ui/src/fieldset.ts +++ b/packages/ui/src/fieldset.ts @@ -2,36 +2,61 @@ import { Fieldset } from "@foldkit/ui"; import type { Html, HtmlBuilder } from "foldkit/html"; import * as Description from "./description"; -import { rootAttrs, slotAttrs, type Children, type StyledConfig, type WithSlotProps } from "./catalog.shared"; +import { + rootAttrs, + slotAttrs, + type Children, + type StyledConfig, + type WithSlotProps, +} from "./catalog.shared"; import { fieldsetStyles } from "./styles"; export type Slot = "root" | "legend" | "description" | "content"; -export type ViewConfig = StyledConfig & WithSlotProps & Readonly<{ - id: string; - legend: string; - description?: string; - children: Children; - isDisabled?: boolean; -}>; +export type ViewConfig = StyledConfig & + WithSlotProps & + Readonly<{ + id: string; + legend: string; + description?: string; + children: Children; + isDisabled?: boolean; + }>; -export const view = ( - config: ViewConfig, - h: HtmlBuilder, -): Html => Fieldset.view( - { - id: config.id, - ...(config.isDisabled === undefined ? {} : { isDisabled: config.isDisabled }), - toView: (attributes) => h.fieldset( - [...attributes.fieldset, ...rootAttrs(config, h, - fieldsetStyles.root, config.isDisabled === true && fieldsetStyles.disabled)], - [ - h.legend([...attributes.legend, ...slotAttrs(config.slotProps?.legend, h, fieldsetStyles.legend)], [config.legend]), - Description.view(config.description, attributes.description, - slotAttrs(config.slotProps?.description, h, fieldsetStyles.description), h), - h.div(slotAttrs(config.slotProps?.content, h, fieldsetStyles.content), config.children), - ], - ), - }, - h, -); +export const view = (config: ViewConfig, h: HtmlBuilder): Html => + Fieldset.view( + { + id: config.id, + hasDescription: true, + ...(config.isDisabled === undefined ? {} : { isDisabled: config.isDisabled }), + toView: (attributes) => + h.fieldset( + [ + ...attributes.fieldset, + ...rootAttrs( + config, + h, + fieldsetStyles.root, + config.isDisabled === true && fieldsetStyles.disabled, + ), + ], + [ + h.legend( + [ + ...attributes.legend, + ...slotAttrs(config.slotProps?.legend, h, fieldsetStyles.legend), + ], + [config.legend], + ), + Description.view( + config.description, + attributes.description, + slotAttrs(config.slotProps?.description, h, fieldsetStyles.description), + h, + ), + h.div(slotAttrs(config.slotProps?.content, h, fieldsetStyles.content), config.children), + ], + ), + }, + h, + ); diff --git a/packages/ui/src/foldkit-attribute.test.ts b/packages/ui/src/foldkit-attribute.test.ts index 4300427..9ca7ebf 100644 --- a/packages/ui/src/foldkit-attribute.test.ts +++ b/packages/ui/src/foldkit-attribute.test.ts @@ -1,7 +1,7 @@ import type { Attribute } from "foldkit/html"; import { describe, expectTypeOf, it } from "vitest"; -// patches/foldkit@0.156.0.patch turns Foldkit's 310-variant `Attribute` +// patches/foldkit@0.168.0.patch turns Foldkit's 310-variant `Attribute` // union into one covariant interface. As a union, every generic view that infers // `Message` from an attribute array compares two such unions member by member, // which pushed TypeScript's identity cache for this package to V8's Map limit diff --git a/packages/ui/src/stateful.test.ts b/packages/ui/src/stateful.test.ts index f2d6812..a50a35a 100644 --- a/packages/ui/src/stateful.test.ts +++ b/packages/ui/src/stateful.test.ts @@ -9,33 +9,49 @@ import * as Select from "./stateful/select"; import * as Tooltip from "./stateful/tooltip"; const TestTabs = Tabs.create<"overview" | "restricted" | "activity">(); -const tabInputs = Tabs.styledViewInputs({ - ariaLabel: "Account views", - selectedValue: "overview", - tabs: [ - { value: "overview", label: "Overview", content: ["Overview content"] }, - { value: "restricted", label: "Restricted", content: ["Restricted content"], isDisabled: true }, - { value: "activity", label: "Activity", content: ["Activity content"] }, - ], -}, h); +const tabInputs = Tabs.styledViewInputs( + { + ariaLabel: "Account views", + selectedValue: "overview", + tabs: [ + { value: "overview", label: "Overview", content: ["Overview content"] }, + { + value: "restricted", + label: "Restricted", + content: ["Restricted content"], + isDisabled: true, + }, + { value: "activity", label: "Activity", content: ["Activity content"] }, + ], + }, + h, +); describe("styled Foldkit tabs", () => { it("applies attributes to repeated and structural slots", () => { - const inputs = Tabs.styledViewInputs({ - ariaLabel: "Composable views", - selectedValue: "overview", - tabs: [ - { value: "overview", label: "Overview", content: ["Overview content"] }, - { value: "restricted", label: "Restricted", content: ["Restricted content"], isDisabled: true }, - { value: "activity", label: "Activity", content: ["Activity content"] }, - ], - slotProps: { - list: { attributes: [h.DataAttribute("composition-slot", "list")] }, - trigger: { attributes: [h.DataAttribute("composition-slot", "trigger")] }, - panel: { attributes: [h.DataAttribute("composition-slot", "panel")] }, + const inputs = Tabs.styledViewInputs( + { + ariaLabel: "Composable views", + selectedValue: "overview", + tabs: [ + { value: "overview", label: "Overview", content: ["Overview content"] }, + { + value: "restricted", + label: "Restricted", + content: ["Restricted content"], + isDisabled: true, + }, + { value: "activity", label: "Activity", content: ["Activity content"] }, + ], + slotProps: { + list: { attributes: [h.DataAttribute("composition-slot", "list")] }, + trigger: { attributes: [h.DataAttribute("composition-slot", "trigger")] }, + panel: { attributes: [h.DataAttribute("composition-slot", "panel")] }, + }, }, - }, h); - Scene.scene( + h, + ); + Scene.scene( { update: TestTabs.update, view: Scene.withViewInputs(TestTabs.view, inputs)() }, Scene.given(Tabs.init({ id: "composable-tabs" })), Scene.expect(Scene.selector('[data-composition-slot="list"]')).toExist(), @@ -45,11 +61,14 @@ describe("styled Foldkit tabs", () => { }); it("navigates past disabled tabs and emits the selected value through the real key handler", () => { - Scene.scene( + Scene.scene( { update: TestTabs.update, view: Scene.withViewInputs(TestTabs.view, tabInputs)() }, Scene.given(Tabs.init({ id: "account" })), Scene.expect(Scene.role("tab", { name: "Restricted" })).toBeDisabled(), - Scene.expect(Scene.role("tab", { name: "Overview" })).toHaveAttr("aria-controls", "account-panel-0"), + Scene.expect(Scene.role("tab", { name: "Overview" })).toHaveAttr( + "aria-controls", + "account-panel-0", + ), Scene.keydown(Scene.role("tab", { name: "Overview" }), "ArrowRight"), Scene.expectOutMessage(Tabs.OutMessage.Selected({ value: "activity", index: 2 })), Scene.Command.resolve(Tabs.FocusTab, Tabs.Message.CompletedFocusTab()), @@ -57,7 +76,7 @@ describe("styled Foldkit tabs", () => { }); it("keeps manual keyboard focus separate from selection until Enter", () => { - Scene.scene( + Scene.scene( { update: TestTabs.update, view: Scene.withViewInputs(TestTabs.view, tabInputs)() }, Scene.given(Tabs.init({ id: "account", activationMode: "Manual" })), Scene.keydown(Scene.role("tab", { name: "Overview" }), "End"), @@ -73,7 +92,7 @@ describe("styled Foldkit tabs", () => { it("uses vertical arrow navigation when configured", () => { const inputs = { ...tabInputs, orientation: "Vertical" as const }; - Scene.scene( + Scene.scene( { update: TestTabs.update, view: Scene.withViewInputs(TestTabs.view, inputs)() }, Scene.given(Tabs.init({ id: "account" })), Scene.expect(Scene.role("tablist")).toHaveAttr("aria-orientation", "vertical"), @@ -86,55 +105,72 @@ describe("styled Foldkit tabs", () => { describe("styled Foldkit dialog", () => { it("applies slot attributes without replacing engine attributes", () => { - const inputs = Dialog.styledViewInputs({ - title: "Composable dialog", - content: () => ["Content"], - slotProps: { - panel: { attributes: [h.DataAttribute("composition-slot", "panel")] }, - title: { attributes: [h.DataAttribute("composition-slot", "title")] }, + const inputs = Dialog.styledViewInputs( + { + title: "Composable dialog", + content: () => ["Content"], + slotProps: { + panel: { attributes: [h.DataAttribute("composition-slot", "panel")] }, + title: { attributes: [h.DataAttribute("composition-slot", "title")] }, + }, }, - }, h); + h, + ); const model = { ...Dialog.init({ id: "composable-dialog" }), isOpen: true }; Scene.scene( { update: Dialog.update, view: Scene.withViewInputs(Dialog.view, inputs)() }, Scene.given(model), + Scene.Mount.resolve(Dialog.AcquireResources, Dialog.Message.SucceededAcquireResources()), Scene.expect(Scene.selector('[data-composition-slot="panel"]')).toExist(), - Scene.expect(Scene.selector('[data-composition-slot="title"]')) - .toHaveAttr("id", Dialog.titleId(model)), + Scene.expect(Scene.selector('[data-composition-slot="title"]')).toHaveAttr( + "id", + Dialog.titleId(model), + ), ); }); it("retains initial focus, description linkage, and the engine's close command", () => { - const inputs = Dialog.styledViewInputs({ - title: "Edit profile", - content: ({ initialFocus }, h) => [h.input([...initialFocus, h.AriaLabel("Name")])], - footer: ({ closeButton }, h) => [h.button(closeButton, ["Cancel"])], - }, h); + const inputs = Dialog.styledViewInputs( + { + title: "Edit profile", + content: ({ initialFocus }, h) => [h.input([...initialFocus, h.AriaLabel("Name")])], + footer: ({ closeButton }, h) => [h.button(closeButton, ["Cancel"])], + }, + h, + ); const model = { ...Dialog.init({ id: "profile" }), isOpen: true }; Scene.scene( { update: Dialog.update, view: Scene.withViewInputs(Dialog.view, inputs)() }, Scene.given(model), + Scene.Mount.resolve(Dialog.AcquireResources, Dialog.Message.SucceededAcquireResources()), Scene.expect(Scene.role("dialog")).toHaveAttr("aria-labelledby", Dialog.titleId(model)), Scene.expect(Scene.selector(`[id="${Dialog.descriptionId(model)}"]`)).toExist(), Scene.expect(Scene.role("button", { name: "Cancel" })).toHaveAttr("type", "button"), Scene.click(Scene.role("button", { name: "Cancel" })), Scene.expectOutMessage(Dialog.OutMessage.Closed()), Scene.Command.resolve(Dialog.CloseDialog, Dialog.Message.CompletedCloseDialog()), + Scene.Mount.expectEnded(Dialog.AcquireResources), ); }); it("supports rich titles, embedded close actions, dividers, and width presets", () => { - const inputs = Dialog.styledViewInputs({ - title: [h.span([], ["Edit schema"])], - size: "md", - dividers: true, - headerActions: ({ closeButton }, h) => [h.button([...closeButton, h.AriaLabel("Close editor")], ["×"])], - content: () => ["Editor"], - }, h); + const inputs = Dialog.styledViewInputs( + { + title: [h.span([], ["Edit schema"])], + size: "md", + dividers: true, + headerActions: ({ closeButton }, h) => [ + h.button([...closeButton, h.AriaLabel("Close editor")], ["×"]), + ], + content: () => ["Editor"], + }, + h, + ); const model = { ...Dialog.init({ id: "schema-editor" }), isOpen: true }; Scene.scene( { update: Dialog.update, view: Scene.withViewInputs(Dialog.view, inputs)() }, Scene.given(model), + Scene.Mount.resolve(Dialog.AcquireResources, Dialog.Message.SucceededAcquireResources()), Scene.expect(Scene.role("heading", { name: "Edit schema" })).toExist(), Scene.expect(Scene.role("button", { name: "Close editor" })).toExist(), Scene.expect(Scene.selector("section")).toHaveAttr("data-size", "md"), @@ -145,11 +181,15 @@ describe("styled Foldkit dialog", () => { describe("styled Foldkit tooltip", () => { it("opens on focus, dismisses with Escape, and supports a custom trigger element", () => { - const inputs = Tooltip.styledViewInputs({ - trigger: ["Schema help"], - label: [h.strong([], ["Choose a valid schema"])], - renderTrigger: (attributes, children, h) => h.span([...attributes, h.Tabindex(0)], children), - }, h); + const inputs = Tooltip.styledViewInputs( + { + trigger: ["Schema help"], + label: [h.strong([], ["Choose a valid schema"])], + renderTrigger: (attributes, children, h) => + h.span([...attributes, h.Tabindex(0)], children), + }, + h, + ); const trigger = Scene.selector(`#${Tooltip.triggerId("schema-help")}`); Scene.scene( { update: Tooltip.update, view: Scene.withViewInputs(Tooltip.view, inputs)() }, @@ -169,16 +209,28 @@ describe("styled Foldkit select", () => { { value: "people", label: "People" }, { value: "operations", label: "Operations", isDisabled: true }, ] as const; - const inputs = Select.styledViewInputs({ - options, value: "people", ariaLabel: "Department", name: "department", - }, h); + const inputs = Select.styledViewInputs( + { + options, + value: "people", + ariaLabel: "Department", + name: "department", + }, + h, + ); const TestSelect = Select.create<"people" | "operations">(); it("passes disabled behavior and form values to the listbox engine", () => { Scene.scene( - { update: TestSelect.update, view: Scene.withViewInputs(TestSelect.view, { ...inputs, isDisabled: true })() }, + { + update: TestSelect.update, + view: Scene.withViewInputs(TestSelect.view, { ...inputs, isDisabled: true })(), + }, Scene.given(Select.init({ id: "department" })), - Scene.expect(Scene.role("button", { name: "Department" })).toHaveAttr("aria-disabled", "true"), + Scene.expect(Scene.role("button", { name: "Department" })).toHaveAttr( + "aria-disabled", + "true", + ), Scene.expect(Scene.selector('input[name="department"]')).toHaveAttr("value", "people"), Scene.expectNoOutMessage(), Scene.expect(Scene.role("listbox")).not.toExist(), diff --git a/packages/ui/src/switch.ts b/packages/ui/src/switch.ts index 38cb296..b5ec838 100644 --- a/packages/ui/src/switch.ts +++ b/packages/ui/src/switch.ts @@ -7,50 +7,83 @@ import { switchStyles } from "./styles"; export type Slot = "root" | "copy" | "label" | "description" | "control" | "thumb" | "hiddenInput"; -export type ViewConfig = StyledConfig & WithSlotProps & Readonly<{ - id: string; - label: string; - description?: string; - isChecked: boolean; - onToggle: (isChecked: boolean) => Message; - isDisabled?: boolean; - isReadOnly?: boolean; - name?: string; - value?: string; -}>; +export type ViewConfig = StyledConfig & + WithSlotProps & + Readonly<{ + id: string; + label: string; + description?: string; + isChecked: boolean; + onToggle: (isChecked: boolean) => Message; + isDisabled?: boolean; + isReadOnly?: boolean; + name?: string; + value?: string; + }>; -export const view = ( - config: ViewConfig, - h: HtmlBuilder, -): Html => Switch.view( - { - id: config.id, - isChecked: config.isChecked, - onToggle: config.onToggle, - ...(config.isDisabled === undefined ? {} : { isDisabled: config.isDisabled }), - ...(config.isReadOnly === undefined ? {} : { isReadOnly: config.isReadOnly }), - ...(config.name === undefined ? {} : { name: config.name }), - ...(config.value === undefined ? {} : { value: config.value }), - toView: (attributes) => h.div( - rootAttrs(config, h, switchStyles.root, config.isDisabled === true && switchStyles.disabled), - [ - h.div(slotAttrs(config.slotProps?.copy, h, switchStyles.copy), [ - h.span([...attributes.label, ...slotAttrs(config.slotProps?.label, h, switchStyles.label)], [config.label]), - Description.view(config.description, attributes.description, - slotAttrs(config.slotProps?.description, h, switchStyles.description), h), - ]), - h.button( - [...attributes.button, ...slotAttrs(config.slotProps?.control, h, - switchStyles.control, config.isChecked && switchStyles.checked)], - [h.span(slotAttrs(config.slotProps?.thumb, h, - switchStyles.thumb, config.isChecked && switchStyles.thumbChecked))], +export const view = (config: ViewConfig, h: HtmlBuilder): Html => + Switch.view( + { + id: config.id, + hasDescription: true, + isChecked: config.isChecked, + onToggle: config.onToggle, + ...(config.isDisabled === undefined ? {} : { isDisabled: config.isDisabled }), + ...(config.isReadOnly === undefined ? {} : { isReadOnly: config.isReadOnly }), + ...(config.name === undefined ? {} : { name: config.name }), + ...(config.value === undefined ? {} : { value: config.value }), + toView: (attributes) => + h.div( + rootAttrs( + config, + h, + switchStyles.root, + config.isDisabled === true && switchStyles.disabled, + ), + [ + h.div(slotAttrs(config.slotProps?.copy, h, switchStyles.copy), [ + h.span( + [...attributes.label, ...slotAttrs(config.slotProps?.label, h, switchStyles.label)], + [config.label], + ), + Description.view( + config.description, + attributes.description, + slotAttrs(config.slotProps?.description, h, switchStyles.description), + h, + ), + ]), + h.button( + [ + ...attributes.button, + ...slotAttrs( + config.slotProps?.control, + h, + switchStyles.control, + config.isChecked && switchStyles.checked, + ), + ], + [ + h.span( + slotAttrs( + config.slotProps?.thumb, + h, + switchStyles.thumb, + config.isChecked && switchStyles.thumbChecked, + ), + ), + ], + ), + ...(attributes.hiddenInput.length === 0 + ? [] + : [ + h.input([ + ...attributes.hiddenInput, + ...slotAttrs(config.slotProps?.hiddenInput, h, switchStyles.hiddenInput), + ]), + ]), + ], ), - ...(attributes.hiddenInput.length === 0 - ? [] - : [h.input([...attributes.hiddenInput, - ...slotAttrs(config.slotProps?.hiddenInput, h, switchStyles.hiddenInput)])]), - ], - ), - }, - h, -); + }, + h, + ); diff --git a/patches/foldkit@0.156.0.patch b/patches/foldkit@0.156.0.patch deleted file mode 100644 index 37ea6c2..0000000 --- a/patches/foldkit@0.156.0.patch +++ /dev/null @@ -1,49 +0,0 @@ -diff --git a/dist/html/index.d.ts b/dist/html/index.d.ts -index e5c78793c16c03a3f76cd79e95de9fcc8c7e47bc..b12803adea74f5eabe5b7e8f2a1783533bdee8c1 100644 ---- a/dist/html/index.d.ts -+++ b/dist/html/index.d.ts -@@ -149,7 +149,15 @@ export type FoldkitMountMarker = Readonly<{ - args?: Record; - messageMappers?: ReadonlyArray<(message: unknown) => unknown>; - }>; --/** Union of all HTML, SVG, and MathML attributes a virtual DOM element can carry. -+/** Any HTML, SVG, or MathML attribute a virtual DOM element can carry, -+ * typed by the Message its handlers dispatch. -+ * -+ * Attributes are built with the `h` constructors (`h.Class`, `h.OnClick`), -+ * which return the precise variant for their `_tag`. `Attribute` is -+ * the one shape every variant is assignable to: it checks the tag, rejects -+ * unknown fields, and carries `Message` covariantly, so an `Attribute` -+ * (a static attribute, or anything from {@link inertHtml}) fits any Message -+ * universe while a handler for another universe does not. - * - * When a Submodel publishes attribute groups to a consumer's `toView` - * slot, those attributes are wrapped via {@link childAttributes} into -@@ -158,7 +166,26 @@ export type FoldkitMountMarker = Readonly<{ - * `ReadonlyArray | ChildAttribute>`, so consumers - * can spread published bundles directly into their own attribute - * arrays. */ --export type Attribute = Data.TaggedEnum<{ -+export interface Attribute { -+ readonly _tag: AttributeTag; -+ readonly key?: string; -+ readonly value?: unknown; -+ readonly text?: string; -+ readonly name?: string; -+ readonly message?: Message; -+ readonly options?: ClickOptions; -+ readonly f?: (...args: never) => Message | Option.Option | Option.Option>; -+ readonly action?: MountAction; -+} -+/** The `_tag` of every attribute variant. */ -+export type AttributeTag = AttributeVariant['_tag']; -+/** The precise runtime shape of every attribute, one variant per `_tag`. -+ * Every variant is assignable to {@link Attribute}; the renderer switches on -+ * this union. */ -+export type AttributeVariant = Data.TaggedEnum<{ - Key: { - readonly value: string; - }; diff --git a/patches/foldkit@0.168.0.patch b/patches/foldkit@0.168.0.patch new file mode 100644 index 0000000..79507cb --- /dev/null +++ b/patches/foldkit@0.168.0.patch @@ -0,0 +1,30 @@ +--- a/dist/html/index.d.ts ++++ b/dist/html/index.d.ts +@@ -171,7 +171,26 @@ + * `ReadonlyArray | ChildAttribute>`, so consumers + * can spread published bundles directly into their own attribute + * arrays. */ +-export type Attribute = Data.TaggedEnum<{ ++export interface Attribute { ++ readonly _tag: AttributeTag; ++ readonly key?: string; ++ readonly value?: unknown; ++ readonly text?: string; ++ readonly name?: string; ++ readonly message?: Message; ++ readonly options?: ClickOptions; ++ readonly f?: (...args: never) => Message | Option.Option | Option.Option>; ++ readonly action?: MountAction; ++} ++/** The `_tag` of every attribute variant. */ ++export type AttributeTag = AttributeVariant['_tag']; ++/** The precise runtime shape of every attribute, one variant per `_tag`. ++ * Every variant is assignable to {@link Attribute}; the renderer switches on ++ * this union. */ ++export type AttributeVariant = Data.TaggedEnum<{ + Key: { + readonly value: string; + }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd1407d..1d1592c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,18 +6,24 @@ settings: catalogs: default: + '@effect/platform-browser': + specifier: 4.0.3 + version: 4.0.3 + '@effect/platform-node': + specifier: 4.0.3 + version: 4.0.3 '@foldkit/devtools': - specifier: 0.156.0 - version: 0.156.0 + specifier: 0.168.0 + version: 0.168.0 '@foldkit/markdown': - specifier: 0.9.0 - version: 0.9.0 + specifier: 0.14.3 + version: 0.14.3 '@foldkit/ui': - specifier: 0.156.0 - version: 0.156.0 + specifier: 0.168.0 + version: 0.168.0 '@foldkit/vite-plugin': - specifier: 0.20.0 - version: 0.20.0 + specifier: 0.27.1 + version: 0.27.1 '@lucide/icons': specifier: 1.40.0 version: 1.40.0 @@ -40,11 +46,11 @@ catalogs: specifier: 2.37.3 version: 2.37.3 effect: - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112 + specifier: 4.0.3 + version: 4.0.3 foldkit: - specifier: 0.156.0 - version: 0.156.0 + specifier: 0.168.0 + version: 0.168.0 inter-font: specifier: 3.19.0 version: 3.19.0 @@ -64,36 +70,45 @@ catalogs: specifier: 8.2.2 version: 8.2.2 vitest: - specifier: 4.1.11 - version: 4.1.11 + specifier: 5.0.3 + version: 5.0.3 + +overrides: + '@foldkit/vite-plugin@0.27.1>@effect/platform-node': 4.0.3 + '@foldkit/vite-plugin@0.27.1>@effect/platform-node-shared': 4.0.3 + alchemy@2.0.0-beta.80>@effect/sql-d1: 4.0.3 + alchemy@2.0.0-beta.80>@effect/sql-sqlite-do: 4.0.3 patchedDependencies: - foldkit@0.156.0: - hash: 2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57 - path: patches/foldkit@0.156.0.patch + foldkit@0.168.0: + hash: b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98 + path: patches/foldkit@0.168.0.patch importers: .: devDependencies: '@alchemy.run/frontend-frameworks': - specifier: 2.0.0-beta.76 - version: 2.0.0-beta.76(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) + specifier: 2.0.0-beta.80 + version: 2.0.0-beta.80(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) '@changesets/cli': specifier: ^3.0.2 version: 3.0.2 '@effect/platform-node': - specifier: 4.0.0-rc.112 - version: 4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1) + specifier: 'catalog:' + version: 4.0.3(effect@4.0.3)(redis@6.2.1) + '@effect/vitest': + specifier: 4.0.3 + version: 4.0.3(effect@4.0.3)(vitest@5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))) '@types/node': specifier: 'catalog:' version: 24.13.3 alchemy: - specifier: 2.0.0-beta.76 - version: 2.0.0-beta.76(@alchemy.run/frontend-frameworks@2.0.0-beta.76(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))(vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))(ws@8.21.3) + specifier: 2.0.0-beta.80 + version: 2.0.0-beta.80(@alchemy.run/frontend-frameworks@2.0.0-beta.80(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@effect/vitest@4.0.3(effect@4.0.3)(vitest@5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))))(@types/node@24.13.3)(effect@4.0.3)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))(ws@8.22.0) effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 hono: specifier: 4.12.14 version: 4.12.14 @@ -112,15 +127,18 @@ importers: typescript: specifier: 'catalog:' version: 7.0.2 + vitest: + specifier: 'catalog:' + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) apps/demo: dependencies: '@foldkit/markdown': specifier: 'catalog:' - version: 0.9.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 0.14.3(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) '@foldkit/ui': specifier: 'catalog:' - version: 0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + version: 0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) '@foldworks/agent': specifier: workspace:* version: link:../../packages/agent @@ -186,20 +204,23 @@ importers: version: 0.19.0 effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) pdf-lib: specifier: 'catalog:' version: 1.17.1 devDependencies: + '@effect/platform-browser': + specifier: 'catalog:' + version: 4.0.3(effect@4.0.3) '@foldkit/devtools': specifier: 'catalog:' - version: 0.156.0(@effect/platform-browser@4.0.0-rc.112(effect@4.0.0-rc.112))(@foldkit/ui@0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)))(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + version: 0.168.0(@effect/platform-browser@4.0.3(effect@4.0.3))(@foldkit/ui@0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)))(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) '@foldkit/vite-plugin': specifier: 'catalog:' - version: 0.20.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 0.27.1(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3))(redis@6.2.1)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) '@playwright/test': specifier: 'catalog:' version: 1.62.1 @@ -229,7 +250,7 @@ importers: version: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0) vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/agent: dependencies: @@ -245,16 +266,16 @@ importers: version: 0.19.0 effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/code-editor: dependencies: @@ -267,16 +288,16 @@ importers: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/codebase: devDependencies: @@ -288,7 +309,7 @@ importers: version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/data-grid: dependencies: @@ -301,16 +322,16 @@ importers: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/data-table: dependencies: @@ -323,43 +344,43 @@ importers: devDependencies: foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/diagram: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/diff-viewer: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/editor: dependencies: @@ -390,31 +411,31 @@ importers: version: 4.0.4 effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/form-builder: devDependencies: '@foldkit/ui': specifier: 'catalog:' - version: 0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + version: 0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/generative-ui: dependencies: @@ -427,28 +448,28 @@ importers: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/history: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/keyboard: devDependencies: @@ -457,13 +478,13 @@ importers: version: 24.13.3 effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/outliner: dependencies: @@ -476,16 +497,16 @@ importers: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/pdf: dependencies: @@ -495,16 +516,16 @@ importers: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/pdf-annotator: dependencies: @@ -523,19 +544,19 @@ importers: devDependencies: '@foldkit/ui': specifier: 'catalog:' - version: 0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + version: 0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/pdf-viewer: dependencies: @@ -551,16 +572,16 @@ importers: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/query-builder: dependencies: @@ -573,22 +594,22 @@ importers: devDependencies: '@foldkit/ui': specifier: 'catalog:' - version: 0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + version: 0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) '@stylexjs/stylex': specifier: 'catalog:' version: 0.19.0 effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/sidebar: dependencies: @@ -604,31 +625,31 @@ importers: version: 0.19.0 effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/text-intelligence: devDependencies: effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/ui: dependencies: @@ -641,7 +662,7 @@ importers: devDependencies: '@foldkit/ui': specifier: 'catalog:' - version: 0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + version: 0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) '@stylexjs/stylex': specifier: 'catalog:' version: 0.19.0 @@ -650,16 +671,16 @@ importers: version: 24.13.3 effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 foldkit: specifier: 'catalog:' - version: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + version: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages/workflow: dependencies: @@ -669,30 +690,26 @@ importers: devDependencies: '@foldkit/ui': specifier: 'catalog:' - version: 0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + version: 0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) effect: specifier: 'catalog:' - version: 4.0.0-rc.112 + version: 4.0.3 typescript: specifier: 'catalog:' version: 7.0.2 vitest: specifier: 'catalog:' - version: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + version: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) packages: - '@alcalzone/ansi-tokenize@0.2.5': - resolution: {integrity: sha512-3NX/MpTdroi0aKz134A6RC2Gb2iXVECN4QaAXnvCIxxIm3C3AVB1mkUe8NaaiyvOpDfsrqWhYtj+Q6a62RrTsw==} - engines: {node: '>=18'} - - '@alchemy.run/cloudflare-runtime@2.0.0-beta.76': - resolution: {integrity: sha512-rH5BFh0Xq7aExpSpITyQbC+Jqfc9XgwJXidHVHTi1tNRC6/KAWg4tOzBfwiFIxljFLLYcApzlYFvLa/fTYcEjw==} + '@alchemy.run/cloudflare-runtime@2.0.0-beta.80': + resolution: {integrity: sha512-vrMDVAQ+LlzFGkgfIbi/65iwskdSxxWYhJX03sFL72mvkGxvb8RQSKrbxLGHhMz0QSZh796dOff1z9zOwxsIbA==} peerDependencies: - '@distilled.cloud/cloudflare': 1.0.0-rc.8 - '@effect/platform-bun': '>=4.0.0-rc.112 || >=4.0.0' - '@effect/platform-node': '>=4.0.0-rc.112 || >=4.0.0' - effect: '>=4.0.0-rc.112 || >=4.0.0' + '@distilled.cloud/cloudflare': 1.0.0-rc.13 + '@effect/platform-bun': ^4.0.0 + '@effect/platform-node': ^4.0.0 + effect: ^4.0.0 rolldown: 1.2.5 vite: ^7.0.0 || ^8.0.0 peerDependenciesMeta: @@ -705,22 +722,22 @@ packages: vite: optional: true - '@alchemy.run/floci@2.0.0-beta.76': - resolution: {integrity: sha512-UIKd+Hki3t5seDpLfyRBwWiNukKlbAqDCTOYBD+n49b/WQIgEWHhYXzGtnwC7OY/b3KAv5p7gsdn8IpPnRXR8g==} + '@alchemy.run/floci@2.0.0-beta.80': + resolution: {integrity: sha512-gtUXFitVY9pSQQYCDtSVyjfd5YOKY3Wleg3WCG5yWno7/l7xBo4kfOMbM63tzWnmka9LewVSITbMfwnC4D/73A==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@alchemy.run/frontend-frameworks@2.0.0-beta.76': - resolution: {integrity: sha512-GBtF+yj9d97axjazFGx4I7TxH21yrK99reJJ/YNuQTyOn+5rxhXZPUX4ENJz2sCvhkvK3ydPE83nyXMzHKki7A==} + '@alchemy.run/frontend-frameworks@2.0.0-beta.80': + resolution: {integrity: sha512-zi3+18wBj561A7bXqcSv7KqRSgu8Q4iHlQjUkBi0TO/I+CNSx9Sslgbb+7rwhjVysUOXRxLzOzqR7QOVP1yFlw==} peerDependencies: - '@effect/platform-bun': '>=4.0.0-rc.112 || >=4.0.0' - '@effect/platform-node': '>=4.0.0-rc.112 || >=4.0.0' + '@effect/platform-bun': ^4.0.0 + '@effect/platform-node': ^4.0.0 '@octanejs/vite-plugin': '>=0.1.22' '@opennextjs/cloudflare': 1.20.1 - '@sveltejs/kit': '>=3.0.0-next.9' + '@sveltejs/kit': '>=3.0.0-next.27' '@vitejs/plugin-react': ^6.0.2 astro: ^7.0.0 - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 hono: ^4.12.14 nuxt: '>=4.5.0' vite: ^7.0.0 || ^8.0.0 @@ -752,83 +769,20 @@ packages: waku: optional: true - '@alchemy.run/node-utils@2.0.0-beta.76': - resolution: {integrity: sha512-KUAlGdoeWeMEToUP1V3I+mEf6FVzUjU+6Tc8savNvMsXN9t3uwZVqD1FeMzTMzMTJJijjxVljQTCtyoeE9W43w==} - - '@aws-crypto/crc32@5.2.0': - resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} - engines: {node: '>=16.0.0'} - - '@aws-crypto/util@5.2.0': - resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - - '@aws-sdk/core@3.977.9': - resolution: {integrity: sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-cognito-identity@3.972.69': - resolution: {integrity: sha512-vpsh9VWmQVC/nsdzf72F2yUMBOFT1aq+hoLseYV03zjVXVHkjqSNJskFRKPFxc3MRFrHNbSSmOwsbYE15u9ecw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-env@3.972.70': - resolution: {integrity: sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-http@3.972.72': - resolution: {integrity: sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-ini@3.973.15': - resolution: {integrity: sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-login@3.972.77': - resolution: {integrity: sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-node@3.972.82': - resolution: {integrity: sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-process@3.972.70': - resolution: {integrity: sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-sso@3.973.14': - resolution: {integrity: sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-provider-web-identity@3.972.76': - resolution: {integrity: sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/credential-providers@3.1126.0': - resolution: {integrity: sha512-OWu4fqH48e6yUceFs1r0fgC88gpuD/MaFggKoRXXTrjT2VicQQSO9tqlCWd2qmQNAXbwfV8/HGeRm6l9P4FSRA==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/nested-clients@3.997.44': - resolution: {integrity: sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/signature-v4-multi-region@3.996.46': - resolution: {integrity: sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/token-providers@3.1116.0': - resolution: {integrity: sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/types@3.974.5': - resolution: {integrity: sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==} - engines: {node: '>=20.0.0'} - - '@aws-sdk/xml-builder@3.972.40': - resolution: {integrity: sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==} - engines: {node: '>=20.0.0'} + '@alchemy.run/node-utils@2.0.0-beta.80': + resolution: {integrity: sha512-uOhVDQZTjwh1AU4mrk0wB5rQaziTwSrVMfqwFP8wALfU//nveu4goOWZimHtqytXiybENh+iEbALH25EFAkc7A==} - '@aws/lambda-invoke-store@0.3.0': - resolution: {integrity: sha512-sl4Bm6yiMNYrZKkqqDFWN0UfnWhlS8ivKxrYl+6t0gCLrqr8y3B2IqZZbFRkfaVVp7C/baApyh71P+LeE1A2sQ==} - engines: {node: '>=18.0.0'} + '@alchemy.run/sigil@0.1.0-alpha.1': + resolution: {integrity: sha512-RtHELG2w+GluVewRsuKm66idtPzC+4STptMr/61X1Vy5uBkDzcCzjzV8G6jUa4iBv6Il0hHV7UL6wvv//9nD/Q==} + engines: {node: '>=22'} + peerDependencies: + '@types/react': '>=19.2.0' + react-devtools-core: '>=6.1.2' + peerDependenciesMeta: + '@types/react': + optional: true + react-devtools-core: + optional: true '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} @@ -1009,120 +963,155 @@ packages: workerd: optional: true - '@cloudflare/workerd-darwin-64@1.20260704.1': - resolution: {integrity: sha512-XO+vvdhhTNZSsIWCkZ+JaE/JrFUmQAB0H0y/sVkAf32xa2TYBRvFUMwjSqf6WxtlxcKPQvmbLfpO/UQ0l/q4eQ==} + '@cloudflare/workerd-darwin-64@1.20260918.1': + resolution: {integrity: sha512-H5Em6Wd0jjxaloYh2rp+WLBl2eWbkk7nSP1svGt6K1RSv/rNVqmKdpjZPJTBSavOmeYGa88qvtOWwS+33OHTqQ==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260704.1': - resolution: {integrity: sha512-6iI7nbOOO8PzEQ6UZVBZB/hv95m8jl0yvyjMuWrF5cJbiLb5zPw3KnpqvGi+aeOs2ZmUkc81i1FWKfXwLXzPRA==} + '@cloudflare/workerd-darwin-arm64@1.20260918.1': + resolution: {integrity: sha512-CR9JRZEQo93fNgBVF4Df2H2/VYO4n7rxSseSwCVv3bJQEb0huADUSCj2FK4ipxar8ToOEB4wawyw0vJo5U/rQQ==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260704.1': - resolution: {integrity: sha512-3mT0YHtxT7eLjghu3hKSJDUQoz+AYv8FM43nLPYhM0YiHOxr8OlmvnCb2Lp7v/U3bwd3Gnx7WEqjSppR583mGg==} + '@cloudflare/workerd-linux-64@1.20260918.1': + resolution: {integrity: sha512-UQ2nnY3qpXLzQ80frmWO+8HvtqyWaQILe8QYZwpemdjT+sqwCz4Dz+0/WVkFco0v/04kIIqikVeLTY/7gEhmkw==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260704.1': - resolution: {integrity: sha512-Opo7cPTPg4x0WwK+eZSDszCyFLKQkOGNCWxF005HRTJ5SJH8h0K9KyrC1k4LBwx3haXSVi+E1eGqo1gZ1Hmhkg==} + '@cloudflare/workerd-linux-arm64@1.20260918.1': + resolution: {integrity: sha512-4rib51MaLNWweUIUxM/Xj558M5QmyZoBSf0ffv+lYah5VTrvwnez3XGxX72pElnBKHROvAPOi5msQ5Ts8JSI0A==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260704.1': - resolution: {integrity: sha512-a97Ecnzhy04x3U052VKPCNp863F7Wf00WY7Ga5P0SbtYvaO1PDzylsHrvFMPKeiDFcOwqiredawmJ+v6bhIgeQ==} + '@cloudflare/workerd-windows-64@1.20260918.1': + resolution: {integrity: sha512-sATrMx5ShYYgmgUGrcTmvsFSJBFuN95NEkX3xwb1qk8w1A6h5N11sea7yN2IeibwPyPmXKjWNjXOno0hZAM71Q==} engines: {node: '>=16'} cpu: [x64] os: [win32] - '@cloudflare/workers-types@5.20260903.1': - resolution: {integrity: sha512-Dgm28XJqMYj3VCNK14Xwo9UPtSPWL8Izo0emiyeQLZCRXWuhc3GnlKENF4Pf3ot+3NaWosq+yq8OIa531dCr6g==} + '@cloudflare/workers-types@5.20261012.1': + resolution: {integrity: sha512-5Y0NrKXG9CZaNUZ6Dt3TLRE4UQe2/DGuxIhTEVYHUl0jAlv1TpQtI59YBAszRpSaZAMzbM8nqGc1n9Z2q4TVyA==} + + '@distilled.cloud/acme@1.0.0-rc.13': + resolution: {integrity: sha512-3rmzH125jQxk0zpCvXGNEybSaqpAolyCX+8LWDDrXf+wIKs67GflkJyQLjOOh0lKARe29hdUZGgqEbbvpp0LNA==} + peerDependencies: + effect: ^4.0.0 + + '@distilled.cloud/aws@1.0.0-rc.13': + resolution: {integrity: sha512-6ZGcp0LkniB0I2l8Gr7vKRQgTZTch0/07iuvuMZv9Nu+rWwNuZG9VHTUdlIoarWgCHFUNN8yvSYBGOjaeN99tQ==} + peerDependencies: + effect: ^4.0.0 + + '@distilled.cloud/axiom@1.0.0-rc.13': + resolution: {integrity: sha512-aRoJtvfXgWi5dtfjuJvDwnArRKvv2MPYHCxYeZcxZbqe62IaQA5yu/khft7TJvxeDyJoSDnVXZu3RyJjNmBA4g==} + peerDependencies: + effect: ^4.0.0 + + '@distilled.cloud/cloudflare@1.0.0-rc.13': + resolution: {integrity: sha512-Py5FoA6azyYExDb91VaeC+9x+ravOBSRhXeUmpcQjCtgqmOdiXgc2YZGMMPI68DF+kMuHRVIrR+2uRZNDOhYjw==} + peerDependencies: + effect: ^4.0.0 + + '@distilled.cloud/core@1.0.0-rc.13': + resolution: {integrity: sha512-RYyrxW+F5zrTuu1UWV/JoycholBCIkXIusYJdDniDwCdmbjdKGl07KlkkeeY774uBawNGf+pt1ESNGeLVnNHsg==} + peerDependencies: + effect: ^4.0.0 + + '@distilled.cloud/doppler@1.0.0-rc.13': + resolution: {integrity: sha512-GgOEUCc4Bx0C+lYNfZ4OqaA3ULvqDUQTJC8gobSh1L0jfrWclv/kRcSr43v4IXyStld+saUvDfDZgeGzlE74OA==} + peerDependencies: + effect: ^4.0.0 + + '@distilled.cloud/fly-io@1.0.0-rc.13': + resolution: {integrity: sha512-Ipp+9nvs+PzqwDPhZq2/BEHNUeWzXKhrvjfT/eBLFHo/QpH0HgdMH8MSQe6/LGszZoormwA6ZrTv2wxaFO7ZiQ==} + peerDependencies: + effect: ^4.0.0 - '@distilled.cloud/aws@1.0.0-rc.8': - resolution: {integrity: sha512-eL4tRACU4TSykk/6+ItwUTZs93r3tRlrBn36ohqaUxqYYOfmssYlt/SLCzos4nlLgCxb+1T6BAstgYC848v1EQ==} + '@distilled.cloud/gcp@1.0.0-rc.13': + resolution: {integrity: sha512-un8jqil9ejt96oN+OfEDUsbrEOpC6Ut4B/73DqKDttvt0Btal1OeBNTTQsftVCpeEzXAvkYKGRIT2AeEu1J5jA==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/axiom@1.0.0-rc.8': - resolution: {integrity: sha512-lczeM0FPuF1mLZu7V8vcj8Klm9b1VSo4A75PP2a4km/cjphDl9LKamUwrSGLEY9denmqd1nn4+g2Svw1UmGpEw==} + '@distilled.cloud/hetzner@1.0.0-rc.13': + resolution: {integrity: sha512-FvqofgMypNkeZbnjGjWMZqvqGd9GxcMzdbi/6JrbcPaHMJG+CCBGt/mAa7mLlEovzwVaGCsBnOXJ6rP5pZjB4Q==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/cloudflare@1.0.0-rc.8': - resolution: {integrity: sha512-nhEyGOaupi/JmT0PS1DEMH5Ebwu004pvQv3Emzt9MJz3mF3LTdiuAnxP86rf3jsFtfa826TDokKArVMq55OKRA==} + '@distilled.cloud/infisical@1.0.0-rc.13': + resolution: {integrity: sha512-Zz8ksA/Y8UUUnWag2UBxB5WYA6wLqOS78ugWJ3cOr9Ot+4bC0NJYWanq35uFInozLXWhun84USU2wOI+mdwQUQ==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/core@1.0.0-rc.8': - resolution: {integrity: sha512-baZMSaBh9kEyAqdhO+UbkvVDK+wqyI8d0UW0paSp5CqVResOl7ydHU4tCbBUOLoxOrqLRWfmImKrhW9SY1R8qw==} + '@distilled.cloud/neon@1.0.0-rc.13': + resolution: {integrity: sha512-2JvbBFj1kBfkDVPnWcJZ7TJ1cJwWBtScAY7SNJ2qNBUuAK1Wf2kjJXU0/j16eCJ48BQigPArQFWL51wVu/EUQQ==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/fly-io@1.0.0-rc.8': - resolution: {integrity: sha512-0HqVLIpBxmuvrIrnmziz8iK39F5cBxkHlz43WR2s+tFidbPlCzmnci6gxixxiwMBCzcmiRmv57xQxuQzgfDQMw==} + '@distilled.cloud/planetscale@1.0.0-rc.13': + resolution: {integrity: sha512-AM97YGRnDfZdqsxFgQMQ83p7ZlRqxS6/foTTJA5P590UMbbOyrQyFyd33BD0agul8a3ngZde3Oej6v7BN55aWw==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/hetzner@1.0.0-rc.8': - resolution: {integrity: sha512-CAPj912qUQXMetyFvJEc7PJnSG4DR6RBvGXCOfQcJVgGTspt/IdT3iIeIInTUf6rgFRsqoGiRfS4ywTqWoqLFA==} + '@distilled.cloud/prisma@1.0.0-rc.13': + resolution: {integrity: sha512-+UmHSp9JV8XQl5t5tTdQBY+dWZ9abrEoc7leYEctA0Ar1TrKZEl2EaeQDoumrg4RyGaacRlirEbxaDFI4SOPtQ==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/neon@1.0.0-rc.8': - resolution: {integrity: sha512-xIgxgoDyIs/G5lW8T2r4oyHpOcRzBO1wu7p46lU3ppuJ2rlJR4pmP9dF7KxStAVglr9yK3wmmmnraLoCAtd8Sg==} + '@distilled.cloud/railway@1.0.0-rc.13': + resolution: {integrity: sha512-yXalRS3GFLo3hWM0HXptqudi23NVCM4FRD20db2/Ob9FFcNrMJik1fopv+74HOiV+BuxfkCP609KsEK6LVdB/g==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/planetscale@1.0.0-rc.8': - resolution: {integrity: sha512-D0mnkT2Gnk/SKWoz05rkf7+sngH8+5m5ugjQ1GycsFMfTxUVSIVUIHX9yrO5vFrTsMlNsmrMR+7a9R3ZMGCWkA==} + '@distilled.cloud/stripe@1.0.0-rc.13': + resolution: {integrity: sha512-jLTJ1mg4U39lHZb9EGYBJLR91yB3LSG6fVxix+r1BbqaTovWzE+NuJ7fu9fRSeax9jRAqsGLLxe9IxJbOtwQnQ==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 - '@distilled.cloud/railway@1.0.0-rc.8': - resolution: {integrity: sha512-VW2YdbblVHhYww5JVtOjFlv3jBFl7xlt6/xTfkNxxgXgzJ5GYVlD3gPgBIpGy9Pcnv9rVRA6CL7QZeYeu47c9Q==} + '@distilled.cloud/zerossl@1.0.0-rc.13': + resolution: {integrity: sha512-dnxhg39lZjpTdZbHCVYPoUC9E2r8eSHbqiYDhSq2EurVPJcc9YRXlxZR1bP1nniSp078XV8GA+aUECt/Z56aQg==} peerDependencies: - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 '@dual-bundle/import-meta-resolve@4.2.1': resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==} - '@effect/platform-browser@4.0.0-rc.112': - resolution: {integrity: sha512-GSlqNDnjILz2EqOFPhVdMEHxlPq6SGb8+KOpNnLfRvVJjXRTMawEO+v1PCuwt2CHAqESbJAa2Nk+qcQvTrv4MQ==} + '@effect/platform-browser@4.0.3': + resolution: {integrity: sha512-1heMTrwHkghTN2DWRb+Os+YdeowMFaxTbBqPzmqJDGU8DlBCKPGr+f4BIjWmnANxlnsIlKUqzr3gKqfR1ueicw==} peerDependencies: - effect: ^4.0.0-rc.112 + effect: ^4.0.3 - '@effect/platform-node-shared@4.0.0-rc.112': - resolution: {integrity: sha512-ttjz0xKamFN7vL8pNDYVwddJLjZvqKePc05djlz2VcdaKbLsnYbtMnL1rbOfHgEnIUSHGh7FkjaN4DM1Ov81sQ==} + '@effect/platform-node-shared@4.0.3': + resolution: {integrity: sha512-5srkJfmWklWSgwZwlkMeTXobfNtxuwuTCvY9JWdPXRv/PBXAe5kKSptzRxT778oFFsaE9IMI8X61pvkifT9Eug==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-rc.112 + effect: ^4.0.3 - '@effect/platform-node@4.0.0-rc.112': - resolution: {integrity: sha512-/BMAcdNGQQskLmI0Zoa95KfTZkr9HV9N4NSxaSrusG6GeW6Ulp9KvZ+Rlaiw8lnOt43CXjFLdfll5/k5rxL4hQ==} + '@effect/platform-node@4.0.3': + resolution: {integrity: sha512-ctvREpC1EzRVsOm0JyIzYHajAQScFaec6Gl+M7TEIpEqdmmx6Ju11Um92EA/CE2KBfY5Fp1QWlqTkK6lOL7tqg==} engines: {node: '>=18.0.0'} peerDependencies: - effect: ^4.0.0-rc.112 + effect: ^4.0.3 redis: '>=5.0.0 <7.0.0' - '@effect/sql-d1@4.0.0-rc.112': - resolution: {integrity: sha512-Wxdr6aU3pBHqhyBiybzbHmEK7qZ2w4iy0IxBgJHe9BLleYzCxObOaBQI9qq/VZSqEvc0IaOBMpbk2zh3dlPNSQ==} + '@effect/sql-d1@4.0.3': + resolution: {integrity: sha512-PPItxp9aAmJTRIfZtyJMuQCB7d1ExFvIRkGWnfQuWZlPxjtYfQT+V2kS9AUqf0OC1/nZnAFwLlFKCDgumLmy6A==} peerDependencies: - effect: ^4.0.0-rc.112 + effect: ^4.0.3 - '@effect/sql-sqlite-do@4.0.0-rc.112': - resolution: {integrity: sha512-PewNVasimmhrdxYbNU91O0YlCcalIHOoF0H5XAWXrmzcEQrKM7kVCyb5h0gmJAZjBM4ZgWRvPY0PUOfbNmWchQ==} + '@effect/sql-sqlite-do@4.0.3': + resolution: {integrity: sha512-q3DeEnsmAbD2jQX9g3+U0iij7VFVgzPtSLoOwbB9oegeBkzzBXVDrblAvw2NgEfs0OctkaIkJhiYJROqSafQKA==} peerDependencies: - effect: ^4.0.0-rc.112 + effect: ^4.0.3 - '@effect/vitest@4.0.0-rc.112': - resolution: {integrity: sha512-mEKh/FI64mt8JK1/v9mpOrJYdnp+UFZdRUBMEdZMiKz7klg6NPqVgg/oeAGH6wOOQc2iAPcfc2H9BbAv1KyzMQ==} + '@effect/vitest@4.0.3': + resolution: {integrity: sha512-jsIkpssf0CFuOeycW2sQZkWx0qzlsRv+JIkzd9qQpoFDcHC9JZFSdIjEBAhsdp20pDy4Y/b2vT/ew7wXZxvEig==} peerDependencies: - effect: ^4.0.0-rc.112 - vitest: '>=4.1.0 <5.0.0' + effect: ^4.0.3 + vitest: '>=5.0.0 <6.0.0' '@electric-sql/pglite-socket@0.0.20': resolution: {integrity: sha512-J5nLGsicnD9wJHnno9r+DGxfcZWh+YJMCe0q/aCgtG6XOm9Z7fKeite8IZSNXgZeGltSigM9U/vAWZQWdgcSFg==} @@ -1462,37 +1451,37 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} - '@foldkit/devtools@0.156.0': - resolution: {integrity: sha512-w6yysLrXxgc4czVkrm6nom3pMFhvWInJQA3HqnV2m2mNNtqLHPfXzLSXL57SMNHpLqNeUBLHt2jHnpP/6NfDBg==} + '@foldkit/devtools@0.168.0': + resolution: {integrity: sha512-VjVOcrOQhgSdzVbrdG8Fjyz4LpBiPF2a82Gz3y+Z2MtI4b8Gf1IHRQSjtac/ugjkPsqOTGWwTQ7l4QaDrW00cw==} engines: {node: '>=18.0.0'} peerDependencies: - '@effect/platform-browser': 4.0.0-rc.112 - '@foldkit/ui': ^0 - effect: 4.0.0-rc.112 - foldkit: '>=0.153.0' + '@effect/platform-browser': 4.0.0 + '@foldkit/ui': '>=0.167.0' + effect: 4.0.0 + foldkit: '>=0.167.0' - '@foldkit/markdown@0.9.0': - resolution: {integrity: sha512-vZeUAsXPln2Lri/vS5Gf+283u7/rCXkFz5O8qYVBFax3XiDhVHsaFf1zhFHfYnxqV0X5pdB4vvgzg8kC8JD33Q==} + '@foldkit/markdown@0.14.3': + resolution: {integrity: sha512-MuzTntXe0q7N+7Y7g8zu5mNkx8YjXfwacoKc+AyAzGF5xSDzWCentczzuA7Jp2BDYIi5TDFdkL4ls+GJOevxdw==} engines: {node: '>=18.0.0'} peerDependencies: - effect: 4.0.0-rc.112 - foldkit: '>=0.153.0' - vite: ^7.0.0 || ^8.0.0 + effect: 4.0.0 + foldkit: '>=0.165.0' + vite: ^8.0.0 - '@foldkit/ui@0.156.0': - resolution: {integrity: sha512-rnCyUXcs7ScyvCU0s1wL28s6Lq/MhVFYRPXr7N9bn2jvLe9jBrhKgxHe5dlEK4Z7haB5NGUvBL+f2BlnQXbaAQ==} + '@foldkit/ui@0.168.0': + resolution: {integrity: sha512-PZNtVorDO6O45txndwouI8fOFHYcdZ3OP9amJKIR9gWJ13+rx+HaDRHYVnBHNp7fom/7qPEWd25wAJCpQF5i6w==} engines: {node: '>=18.0.0'} peerDependencies: - effect: 4.0.0-rc.112 - foldkit: '>=0.155.0' + effect: 4.0.0 + foldkit: '>=0.167.0' - '@foldkit/vite-plugin@0.20.0': - resolution: {integrity: sha512-rzJocKgnlndHZfj620u0ptJmqBlJmDeZYW3mKy3m4+lKRZHsEB29xeD3sKZuCbDtMmtfl35FOUO9suC8Ub/G4g==} + '@foldkit/vite-plugin@0.27.1': + resolution: {integrity: sha512-sQ8y2v7w21wTwm+NGxboGokbZULmH0kuAg2EV6JH+1rXDBATOCcXIToUmw9ZmaVR14/4SlZIygVnNCRRcDtfQQ==} engines: {node: '>=20.19.0'} peerDependencies: - effect: 4.0.0-rc.112 - foldkit: '>=0.153.0' - vite: ^7.0.0 || ^8.0.0 + effect: 4.0.0 + foldkit: '>=0.167.0' + vite: ^8.0.0 '@hono/node-server@1.19.9': resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} @@ -1743,12 +1732,15 @@ packages: engines: {node: '>=20.9.0'} hasBin: true - '@microsoft/tsdoc-config@0.18.1': - resolution: {integrity: sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==} + '@microsoft/tsdoc-config@0.18.3': + resolution: {integrity: sha512-0I0rGXo1eJziw3EEErkDS9p/0RzBcyOiV+zbSDqOZTeURmhtut+WIvpih0LM5g+ZpZ9SyFxDfkhAcl/r8e2zGw==} '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@microsoft/tsdoc@0.17.1': + resolution: {integrity: sha512-VHyYyyoH63jFoO3shpw9Zfijk5v/9p/VrClW35z0/q6ooK7sSmxpzOkS4iZPxs3vO4XtBW4H38HRAKkAMIK7DA==} + '@modelcontextprotocol/client@2.0.0': resolution: {integrity: sha512-8f1OghQ2rjzIOfqgUCP+8GiUWqRs89njoWLNqAe8kWmDePv3s1fZXseej+QXemssEuuOvLLmLO/kqM3IQHtISw==} engines: {node: '>=20'} @@ -1779,36 +1771,6 @@ packages: resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==} engines: {node: '>=16'} - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': - resolution: {integrity: sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==} - cpu: [arm64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': - resolution: {integrity: sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==} - cpu: [x64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': - resolution: {integrity: sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==} - cpu: [arm64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': - resolution: {integrity: sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==} - cpu: [arm] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': - resolution: {integrity: sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==} - cpu: [x64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': - resolution: {integrity: sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==} - cpu: [x64] - os: [win32] - '@napi-rs/canvas-android-arm64@1.0.8': resolution: {integrity: sha512-5+nkh8i3gt6lqS/d2jTZ1xAn6tdgtB4Lf1mW6T0Qm5/rXNwBuV1sAEyLEWan5o9gJPU/GuvHR3rvSeZ+FaGrbw==} engines: {node: '>= 10'} @@ -1888,20 +1850,14 @@ packages: '@neon-rs/load@0.0.4': resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==} - '@nodable/entities@3.0.0': - resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@neon/functions@0.11.0': + resolution: {integrity: sha512-xyIEk9aULR2nBTzyTgxljkM8aS+Ppe9oVw/t5mdqYOlY/mLFppHSZHq8hiWVDWy6Ca8oLd3loTtfVz+cZQQjsA==} + engines: {node: '>=20.19.0'} + peerDependencies: + hono: ^4.7.8 + peerDependenciesMeta: + hono: + optional: true '@octokit/auth-token@6.0.0': resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} @@ -2623,54 +2579,10 @@ packages: resolution: {integrity: sha512-+jNbxhh8CkrZYxMk9X3GRYM2+Myr1xCCj+eHbJ9Vp+PCdNHS0TUl5QQFT6UbM/aTFRCzs5jiBTKYzBRpe5uYbQ==} engines: {node: '>=20.9.0'} - '@smithy/core@3.33.3': - resolution: {integrity: sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==} - engines: {node: '>=18.0.0'} - - '@smithy/credential-provider-imds@4.5.2': - resolution: {integrity: sha512-A9uSdn72ozbRUSit0eib0TW7nXuNPlaeM0zcGkJ+nE6tFcSDbnmtwoxbTCFBukVQcszDAyvsd7+rTduPTXpygg==} - engines: {node: '>=18.0.0'} - - '@smithy/fetch-http-handler@5.8.0': - resolution: {integrity: sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==} - engines: {node: '>=18.0.0'} - - '@smithy/is-array-buffer@2.2.0': - resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} - engines: {node: '>=14.0.0'} - - '@smithy/node-config-provider@4.6.2': - resolution: {integrity: sha512-zMrXu/O5tPa7GLtra8L4wFG6DACcXT9QV4Ay+WEAjUhXm1dVq7c/q9Qv9gkJZNLY8hmQKg08778kDcxpKNMqOA==} - engines: {node: '>=18.0.0'} - - '@smithy/node-http-handler@4.12.1': - resolution: {integrity: sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==} - engines: {node: '>=18.0.0'} - - '@smithy/shared-ini-file-loader@4.7.2': - resolution: {integrity: sha512-XsIDj5gVG4YRxGS4n4TiBDAogPWRHXKZyor6JW/sEuaa/7BvKADe9j45jI2dPYCnYbKkLBmbZ4qN9ns0sH+kMQ==} - engines: {node: '>=18.0.0'} - - '@smithy/signature-v4@5.7.3': - resolution: {integrity: sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==} - engines: {node: '>=18.0.0'} - '@smithy/types@4.18.0': resolution: {integrity: sha512-CgB6HHWer/vrKps24ulRIbpcpb7K4xAU7SkZ7YHzBPlwHsvsrCJFEXK421s+cJzX+ZrqtA/TuU5w1HzI7k9N8A==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.6.2': - resolution: {integrity: sha512-wTQX1hPfElIqY6AzM/s6c4UgpMxCL4MwJ5u6340ksLPq78lur6Y+keheIDk5gMX4G3KFh4MERcDt6xhRq+ie1Q==} - engines: {node: '>=18.0.0'} - - '@smithy/util-buffer-from@2.2.0': - resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} - engines: {node: '>=14.0.0'} - - '@smithy/util-utf8@2.3.0': - resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} - engines: {node: '>=14.0.0'} - '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -2751,8 +2663,8 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} + '@types/ws@8.18.2': + resolution: {integrity: sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==} '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} @@ -2874,11 +2786,8 @@ packages: cpu: [x64] os: [win32] - '@vitest/expect@4.1.11': - resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} - - '@vitest/mocker@4.1.11': - resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + '@vitest/mocker@5.0.3': + resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2888,26 +2797,19 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/runner@4.1.11': - resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} - - '@vitest/spy@4.1.11': - resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} - - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + '@vitest/spy@5.0.3': + resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} acorn@8.18.0: resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} engines: {node: '>=0.4.0'} hasBin: true + acorn@8.19.0: + resolution: {integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==} + engines: {node: '>=0.4.0'} + hasBin: true + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -2930,25 +2832,28 @@ packages: ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - alchemy@2.0.0-beta.76: - resolution: {integrity: sha512-XpcsXWk38YV9nO0v7s5PPjlMVtF8OQPeP8YqLu1iMzrq33bwViEOqkwQn3cgFiD7/QsnNcCju+hCXyeBwXrdtg==} + alchemy@2.0.0-beta.80: + resolution: {integrity: sha512-2y7mswypPiApxuEdp8NGMfzCGB5+9W6OmQAuePyZ2xBkgoCpEqtKAnASuvKGUZPSYNESENbamXDuI5428ewu9w==} hasBin: true peerDependencies: - '@alchemy.run/frontend-frameworks': 2.0.0-beta.76 + '@alchemy.run/frontend-frameworks': 2.0.0-beta.80 '@aws/durable-execution-sdk-js': ^2.1.0 - '@effect/platform-bun': '>=4.0.0-rc.112 || >=4.0.0' - '@effect/platform-node': '>=4.0.0-rc.112 || >=4.0.0' - '@effect/sql-mysql2': '>=4.0.0-rc.112 || >=4.0.0' - '@effect/sql-pg': '>=4.0.0-rc.112 || >=4.0.0' + '@effect/platform-bun': ^4.0.0 + '@effect/platform-node': ^4.0.0 + '@effect/sql-mysql2': ^4.0.0 + '@effect/sql-pg': ^4.0.0 + '@effect/vitest': ^4.0.0 + '@prisma/orm-postgres': 8.0.0-rc.11 '@vercel/nft': ^1.10.2 drizzle-kit: 1.0.0-rc.5-ab785fc drizzle-orm: 1.0.0-rc.5-ab785fc - effect: '>=4.0.0-rc.112 || >=4.0.0' + effect: ^4.0.0 mongodb: ^6.10.0 mysql2: ^3.24.2 pg: ^8.22.0 + prisma: 8.0.0-rc.15 vite: ^8.0.7 - ws: ^8.20.0 + ws: ^8.21.0 peerDependenciesMeta: '@alchemy.run/frontend-frameworks': optional: true @@ -2962,6 +2867,10 @@ packages: optional: true '@effect/sql-pg': optional: true + '@effect/vitest': + optional: true + '@prisma/orm-postgres': + optional: true '@vercel/nft': optional: true drizzle-kit: @@ -2974,15 +2883,13 @@ packages: optional: true pg: optional: true + prisma: + optional: true vite: optional: true ws: optional: true - ansi-escapes@7.3.0: - resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} - engines: {node: '>=18'} - ansi-regex@6.3.0: resolution: {integrity: sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==} engines: {node: '>=12'} @@ -2994,9 +2901,6 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - anynum@1.0.1: - resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} - argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -3004,13 +2908,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - auto-bind@5.0.1: - resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - aws4fetch@1.0.20: - resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} - bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -3026,17 +2923,10 @@ packages: before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - bowser@2.14.1: - resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} - - brace-expansion@5.0.9: - resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} + brace-expansion@5.0.12: + resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - browserslist@4.28.8: resolution: {integrity: sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3059,17 +2949,17 @@ packages: caniuse-lite@1.0.30001810: resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} - capnp-es@0.0.14: - resolution: {integrity: sha512-8lWj4GJISiqRSlAJGkWpI4Azib7QY5UDIkqxeHcI7aAnXVk9SFuWxl1Fme+2HhzNANV0WTJqwUZvvXvloc3sBA==} + capnp-es@0.0.16: + resolution: {integrity: sha512-pqhhnRqGfDdgHa+rz4JVO9j1jnkyqsHRYrD4RBtIwxCknYSRG7Mjs+x3IRfd20u+ZGS0IhW2L7cDv9ZcIkCYhQ==} hasBin: true peerDependencies: - typescript: ^5.7.3 + typescript: ^5.7.3 || ^6.0.0 peerDependenciesMeta: typescript: optional: true - capnweb@0.6.1: - resolution: {integrity: sha512-fmhV26QPd1ewf5R74h55oVZnGwIcSaRMzbfLQUy8+zOBjuTmT3KXoT8wxHvnp1m9Ht9BoUUS5ZwNLoVLfQTyBg==} + capnweb@0.12.0: + resolution: {integrity: sha512-jgZ/LMtMVTi+RVlooIslH78Gg23XbDTdvcLghWx3oz17D6u5GuJARt+uhZk/wcTo+f81eeabfnow4d3zLBj+JQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -3078,10 +2968,6 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - character-entities-html4@2.1.0: resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} @@ -3101,17 +2987,9 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} - cli-boxes@3.0.0: - resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} - engines: {node: '>=10'} - - cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - cli-truncate@5.2.0: - resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} - engines: {node: '>=20'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} cliui@9.0.1: resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} @@ -3125,10 +3003,6 @@ packages: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} engines: {node: '>=0.10.0'} - code-excerpt@4.0.0: - resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -3147,13 +3021,6 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - convert-to-spaces@2.0.1: - resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3195,8 +3062,8 @@ packages: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} - effect@4.0.0-rc.112: - resolution: {integrity: sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A==} + effect@4.0.3: + resolution: {integrity: sha512-LoWn/Uwwx0JjHSDmBAAahx8sB+klzM6vsZovnbYowBNw6B4QScJLRLBByO61plvNiycx7HPN8FnsWUCu0eXW5w==} electron-to-chromium@1.5.420: resolution: {integrity: sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==} @@ -3208,10 +3075,6 @@ packages: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} - environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} @@ -3219,9 +3082,6 @@ packages: es-module-lexer@2.3.2: resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} - es-toolkit@1.52.0: - resolution: {integrity: sha512-XTNEJQh1tY1ZJVcf6ayP/2n4ZPyaHlW2FWs7xvw5ddPuhUVjLD3olQVQS7kf58JbAB48iL0uL/jerTrjtV3lDA==} - esbuild@0.27.7: resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} @@ -3236,10 +3096,6 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} - escape-string-regexp@2.0.0: - resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} - engines: {node: '>=8'} - escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -3262,39 +3118,21 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - fast-check@4.9.0: - resolution: {integrity: sha512-7ms6T7SybUev/PQITciI0yLM2pOSFy5zpG8Ty7tQofcVaQUvrMXp6CBwqF6fThLCLOrfBtuHAtwq6Yu4XPCllg==} - engines: {node: '>=12.17.0'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - fast-string-truncated-width@3.0.3: resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} fast-string-width@3.0.2: resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - fast-uri@3.1.7: - resolution: {integrity: sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==} + fast-uri@3.1.8: + resolution: {integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==} fast-wrap-ansi@0.2.2: resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} - fast-xml-builder@1.3.1: - resolution: {integrity: sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==} - - fast-xml-parser@5.11.1: - resolution: {integrity: sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==} - hasBin: true - - fastq@1.20.3: - resolution: {integrity: sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==} - fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -3307,18 +3145,18 @@ packages: picomatch: optional: true - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} fix-dts-default-cjs-exports@1.0.1: resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} - foldkit@0.156.0: - resolution: {integrity: sha512-KqzaB0E9S3AzRVR4rN4Xuqj0fzasgmpo52uNnXI26TqV013hwgukGA4+z7+iNuZ2CzqheeiBL3C5MUsk3ewnbg==} + foldkit@0.168.0: + resolution: {integrity: sha512-FyAY34MdmF1JwdKiPq12uKHBmLny3QxAJ6WdakbXCBh8KR1lQfvdKEygQ/XAlxb2ICSmpVaFga5RgDSnxZsFiw==} engines: {node: '>=20.19.0'} peerDependencies: - effect: 4.0.0-rc.112 + '@effect/platform-browser': 4.0.0 + effect: 4.0.0 foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} @@ -3360,10 +3198,6 @@ packages: get-port-please@3.2.0: resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -3373,6 +3207,10 @@ packages: graphmatch@1.1.1: resolution: {integrity: sha512-5ykVn/EXM1hF0XCaWh05VbYvEiOL2lY1kBxZtaYsyvjp7cmWOU1XsAdfQBwClraEofXDT197lFbXOEVMHpvQOg==} + graphql@16.11.0: + resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3396,9 +3234,6 @@ packages: resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} hasBin: true - immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} @@ -3406,26 +3241,6 @@ packages: import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - ink@6.8.0: - resolution: {integrity: sha512-sbl1RdLOgkO9isK42WCZlJCFN9hb++sX9dsklOvfd1YQ3bQ2AiFu12Q6tFlr0HvEUvzraJntQCCpfEoUe9DSzA==} - engines: {node: '>=20'} - peerDependencies: - '@types/react': '>=19.0.0' - react: '>=19.0.0' - react-devtools-core: '>=6.1.2' - peerDependenciesMeta: - '@types/react': - optional: true - react-devtools-core: - optional: true - inter-font@3.19.0: resolution: {integrity: sha512-LKPEweQxslLgp6VaTZuz8vRC8dHsAMy8b28yipwQgcKlYSYJH//lCsLGXpH9cjOQ61K4SiEWCSrItVs8c9As4A==} @@ -3438,47 +3253,20 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-core-module@2.16.2: - resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + is-core-module@2.17.0: + resolution: {integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==} engines: {node: '>= 0.4'} is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@5.1.0: - resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} - engines: {node: '>=18'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} - is-in-ci@2.0.0: - resolution: {integrity: sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w==} - engines: {node: '>=20'} - hasBin: true - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-unsafe@2.0.2: - resolution: {integrity: sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -3520,9 +3308,6 @@ packages: jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} - jszip@3.10.1: - resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - launch-editor@2.14.1: resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} @@ -3537,9 +3322,6 @@ packages: cpu: [x64, arm64, wasm32, arm] os: [darwin, linux, win32] - lie@3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - lightningcss-android-arm64@1.33.0: resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} engines: {node: '>= 12.0.0'} @@ -3641,8 +3423,8 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - magic-string@1.2.3: - resolution: {integrity: sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==} + magic-string@1.4.3: + resolution: {integrity: sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==} markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -3686,10 +3468,6 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} @@ -3780,19 +3558,11 @@ packages: micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime@4.1.0: - resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} + mime@4.1.1: + resolution: {integrity: sha512-YAApRsTpM2+gnUFHhRNGfkX8NK0V5xj1nyQLufGlwEI+eX4xRsTcel2IXDuMEGSGjBvzykNW4VuoEACgwYe7BQ==} engines: {node: '>=16'} hasBin: true - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - minimatch@10.2.3: resolution: {integrity: sha512-Rwi3pnapEqirPSbWbrZaa6N3nmqq4Xer/2XooiOKyV3q12ML06f7MOuc5DVH8ONZIFhwIYQ3yzPH4nt7iWHaTg==} engines: {node: 18 || 20 || >=22} @@ -3807,13 +3577,6 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - msgpackr-extract@3.0.4: - resolution: {integrity: sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==} - hasBin: true - - msgpackr@2.1.0: - resolution: {integrity: sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ==} - mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -3822,10 +3585,6 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} - hasBin: true - node-releases@2.0.54: resolution: {integrity: sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==} engines: {node: '>=18'} @@ -3838,10 +3597,6 @@ packages: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - oxfmt@0.44.0: resolution: {integrity: sha512-lnncqvHewyRvaqdrnntVIrZV2tEddz8lbvPsQzG/zlkfvgZkwy0HP1p/2u1aCDToeg1jb9zBpbJdfkV73Itw+w==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3872,14 +3627,6 @@ packages: parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} - patch-console@2.0.0: - resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - path-expression-matcher@1.6.2: - resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} - engines: {node: '>=14.0.0'} - path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3903,10 +3650,6 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.7: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} @@ -3957,38 +3700,24 @@ packages: resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} engines: {node: ^10 || ^12 || >=14} - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - promise-limit@2.7.0: resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==} proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - pure-rand@8.4.2: - resolution: {integrity: sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - react-reconciler@0.33.0: - resolution: {integrity: sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA==} - engines: {node: '>=0.10.0'} - peerDependencies: - react: ^19.2.0 - react@19.2.8: resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} engines: {node: '>=0.10.0'} - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + redis@6.2.1: resolution: {integrity: sha512-Z9VHtgYs48PiQC77X9O2Er8Hj4T+5BtFjT91/vi5Is1D04N72cA946ZslM1ImJw8ZctFBZWAVjM7S5wJNeHMpg==} engines: {node: '>= 20.0.0'} @@ -4022,23 +3751,15 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} - resolve@1.22.12: - resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} + resolve@1.22.13: + resolution: {integrity: sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==} engines: {node: '>= 0.4'} hasBin: true - restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - retry@0.12.0: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rolldown@1.2.5: resolution: {integrity: sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4054,15 +3775,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -4077,9 +3789,6 @@ packages: engines: {node: '>=10'} hasBin: true - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - sharp@0.35.4: resolution: {integrity: sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==} engines: {node: '>=20.9.0'} @@ -4101,9 +3810,6 @@ packages: resolution: {integrity: sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==} engines: {node: '>= 0.4'} - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -4114,10 +3820,6 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - slice-ansi@8.0.0: - resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} - engines: {node: '>=20'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -4133,12 +3835,10 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stack-utils@2.0.6: - resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} - engines: {node: '>=10'} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + srvx@1.0.5: + resolution: {integrity: sha512-KvSKRpgPG/oaq3cyT614OQ2bAa7DynuGamivCZeoZzIUULOdbQz6wUtnnVuC4+DCx2Zglzo8v5gBYmWIWYhDxA==} + engines: {node: '>=20.16.0'} + hasBin: true std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -4158,9 +3858,6 @@ packages: resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} engines: {node: '>=20'} - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - stringify-entities@4.0.4: resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} @@ -4168,9 +3865,6 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - strnum@2.4.2: - resolution: {integrity: sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==} - styleq@0.2.1: resolution: {integrity: sha512-L0TR0NQb+X4/ktDEKmjWyp27gla+LUYi/by5k5SjKXf6/pvZP7wbwEB5J+tqxdFVPgzbsuz+d4RTScO/QZquBw==} @@ -4187,14 +3881,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tagged-tag@1.0.0: - resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} - engines: {node: '>=20'} - - terminal-size@4.0.1: - resolution: {integrity: sha512-avMLDQpUI9I5XFrklECw1ZEUPJhqzcwSWsyyI8blhRLT+8N1jLJWLWWYQpB2q2xthq8xDvjZPISVh53T/+CLYQ==} - engines: {node: '>=18'} - thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} @@ -4202,8 +3888,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + tinybench@6.2.2: + resolution: {integrity: sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==} + engines: {node: '>=20.0.0'} tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} @@ -4220,14 +3907,6 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} - tinyrainbow@3.1.1: - resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} - engines: {node: '>=14.0.0'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -4267,10 +3946,6 @@ packages: resolution: {integrity: sha512-Pfg2dWNOUPwJYbR+eYAcjoBbo4Kmoc8uQb80hzKrBgNEQtxvwf2ucXG7C28MyLVhV/zJSKvChrR+2CP/BONpoA==} hasBin: true - type-fest@5.9.0: - resolution: {integrity: sha512-yANm3Jr3GiJ1qgJlxGAVxTOIcEOk1rhQHamlXtnrCK7EHP4HeM9OGxtMg/W7HFdrVzw/ZWJKGVIJusVH85sLtw==} - engines: {node: '>=20'} - typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -4287,12 +3962,8 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici@7.29.0: - resolution: {integrity: sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==} - engines: {node: '>=20.18.1'} - - undici@8.10.1: - resolution: {integrity: sha512-YQ3WlbqjYMmNpdvDH64jAgLjxuAR9+649calDWhbshYaeQGO2bR4nI94ORJmwI3J9YhoKQnpyGOK+0zlWS5N5Q==} + undici@8.11.2: + resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} engines: {node: '>=22.19.0'} unenv@2.0.0-rc.24: @@ -4330,9 +4001,6 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - valibot@1.2.0: resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} peerDependencies: @@ -4390,23 +4058,31 @@ packages: yaml: optional: true - vitest@4.1.11: - resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + + vitest@5.0.3: + resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.11 - '@vitest/browser-preview': 4.1.11 - '@vitest/browser-webdriverio': 4.1.11 - '@vitest/coverage-istanbul': 4.1.11 - '@vitest/coverage-v8': 4.1.11 - '@vitest/ui': 4.1.11 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.3 + '@vitest/browser-preview': 5.0.3 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + '@vitest/ui': 5.0.3 happy-dom: '*' jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -4439,17 +4115,13 @@ packages: engines: {node: '>= 8'} hasBin: true - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} hasBin: true - widest-line@6.0.0: - resolution: {integrity: sha512-U89AsyEeAsyoF0zVJBkG9zBgekjgjK7yk9sje3F4IQpXBJ10TF6ByLlIfjMhcmHMJgHZI4KHt4rdNfktzxIAMA==} - engines: {node: '>=20'} - - workerd@1.20260704.1: - resolution: {integrity: sha512-GDZ0jzIYDYfN7rCt/oFJv4BG3QJ+4IS2kfRvxEMY9VKIfPhzo63PH69Ir7ug8LfORCgCtmfkiQVXrqbot7pZTQ==} + workerd@1.20260918.1: + resolution: {integrity: sha512-NsjfQlBNQ0iEniv/STOy4zbp8s5k60PzL1Ter02Eg44arbDhHtf6UOs03E31XDRmmBFxSIkAZuCyld3RKH1wqA==} engines: {node: '>=16'} hasBin: true @@ -4469,9 +4141,17 @@ packages: utf-8-validate: optional: true - xml-naming@0.3.0: - resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==} - engines: {node: '>=16.0.0'} + ws@8.22.0: + resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} @@ -4497,9 +4177,6 @@ packages: resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} engines: {node: '>=12'} - yoga-layout@3.2.1: - resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} - zeptomatch@2.1.0: resolution: {integrity: sha512-KiGErG2J0G82LSpniV0CtIzjlJ10E04j02VOudJsPyPwNZgGnRKQy7I1R7GMyg/QswnE4l7ohSGrQbQbjXPPDA==} @@ -4511,26 +4188,22 @@ packages: snapshots: - '@alcalzone/ansi-tokenize@0.2.5': + '@alchemy.run/cloudflare-runtime@2.0.0-beta.80(@distilled.cloud/cloudflare@1.0.0-rc.13(effect@4.0.3))(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(rolldown@1.2.5)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))': dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - - '@alchemy.run/cloudflare-runtime@2.0.0-beta.76(@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112))(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(rolldown@1.2.5)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))': - dependencies: - '@alchemy.run/node-utils': 2.0.0-beta.76 - '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260704.1) - '@distilled.cloud/cloudflare': 1.0.0-rc.8(effect@4.0.0-rc.112) + '@alchemy.run/node-utils': 2.0.0-beta.80 + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1) + '@distilled.cloud/cloudflare': 1.0.0-rc.13(effect@4.0.3) '@puppeteer/browsers': 3.2.2(yauzl@3.4.0) - capnp-es: 0.0.14(typescript@7.0.2) - effect: 4.0.0-rc.112 + capnp-es: 0.0.16(typescript@7.0.2) + effect: 4.0.3 magic-string: 0.30.21 + mime: 4.1.1 sharp: 0.35.4(@types/node@24.13.3) unenv: 2.0.0-rc.24 - workerd: 1.20260704.1 + workerd: 1.20260918.1 yauzl: 3.4.0 optionalDependencies: - '@effect/platform-node': 4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1) + '@effect/platform-node': 4.0.3(effect@4.0.3)(redis@6.2.1) rolldown: 1.2.5 vite: 8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0) transitivePeerDependencies: @@ -4538,20 +4211,22 @@ snapshots: - proxy-agent - typescript - '@alchemy.run/floci@2.0.0-beta.76(effect@4.0.0-rc.112)': + '@alchemy.run/floci@2.0.0-beta.80(effect@4.0.3)': dependencies: - effect: 4.0.0-rc.112 + effect: 4.0.3 - '@alchemy.run/frontend-frameworks@2.0.0-beta.76(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))': + '@alchemy.run/frontend-frameworks@2.0.0-beta.80(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))': dependencies: - '@alchemy.run/cloudflare-runtime': 2.0.0-beta.76(@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112))(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(rolldown@1.2.5)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) - '@distilled.cloud/cloudflare': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@alchemy.run/cloudflare-runtime': 2.0.0-beta.80(@distilled.cloud/cloudflare@1.0.0-rc.13(effect@4.0.3))(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(rolldown@1.2.5)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) + '@distilled.cloud/aws': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/cloudflare': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 esbuild: 0.28.2 - fast-glob: 3.3.3 rolldown: 1.2.5 + srvx: 1.0.5 + tinyglobby: 0.2.17 optionalDependencies: - '@effect/platform-node': 4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1) + '@effect/platform-node': 4.0.3(effect@4.0.3)(redis@6.2.1) hono: 4.12.14 vite: 8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0) transitivePeerDependencies: @@ -4559,180 +4234,12 @@ snapshots: - proxy-agent - typescript - '@alchemy.run/node-utils@2.0.0-beta.76': {} - - '@aws-crypto/crc32@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.974.5 - tslib: 2.8.1 - - '@aws-crypto/util@5.2.0': - dependencies: - '@aws-sdk/types': 3.974.5 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 - - '@aws-sdk/core@3.977.9': - dependencies: - '@aws-sdk/types': 3.974.5 - '@aws-sdk/xml-builder': 3.972.40 - '@aws/lambda-invoke-store': 0.3.0 - '@smithy/core': 3.33.3 - '@smithy/signature-v4': 5.7.3 - '@smithy/types': 4.18.0 - bowser: 2.14.1 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-cognito-identity@3.972.69': - dependencies: - '@aws-sdk/nested-clients': 3.997.44 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-env@3.972.70': + '@alchemy.run/node-utils@2.0.0-beta.80': dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-http@3.972.72': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/fetch-http-handler': 5.8.0 - '@smithy/node-http-handler': 4.12.1 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-ini@3.973.15': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/credential-provider-env': 3.972.70 - '@aws-sdk/credential-provider-http': 3.972.72 - '@aws-sdk/credential-provider-login': 3.972.77 - '@aws-sdk/credential-provider-process': 3.972.70 - '@aws-sdk/credential-provider-sso': 3.973.14 - '@aws-sdk/credential-provider-web-identity': 3.972.76 - '@aws-sdk/nested-clients': 3.997.44 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/credential-provider-imds': 4.5.2 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-login@3.972.77': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/nested-clients': 3.997.44 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-node@3.972.82': - dependencies: - '@aws-sdk/credential-provider-env': 3.972.70 - '@aws-sdk/credential-provider-http': 3.972.72 - '@aws-sdk/credential-provider-ini': 3.973.15 - '@aws-sdk/credential-provider-process': 3.972.70 - '@aws-sdk/credential-provider-sso': 3.973.14 - '@aws-sdk/credential-provider-web-identity': 3.972.76 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/credential-provider-imds': 4.5.2 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-process@3.972.70': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-sso@3.973.14': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/nested-clients': 3.997.44 - '@aws-sdk/token-providers': 3.1116.0 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-web-identity@3.972.76': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/nested-clients': 3.997.44 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/credential-providers@3.1126.0': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/credential-provider-cognito-identity': 3.972.69 - '@aws-sdk/credential-provider-env': 3.972.70 - '@aws-sdk/credential-provider-http': 3.972.72 - '@aws-sdk/credential-provider-ini': 3.973.15 - '@aws-sdk/credential-provider-login': 3.972.77 - '@aws-sdk/credential-provider-node': 3.972.82 - '@aws-sdk/credential-provider-process': 3.972.70 - '@aws-sdk/credential-provider-sso': 3.973.14 - '@aws-sdk/credential-provider-web-identity': 3.972.76 - '@aws-sdk/nested-clients': 3.997.44 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/credential-provider-imds': 4.5.2 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/nested-clients@3.997.44': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/signature-v4-multi-region': 3.996.46 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/fetch-http-handler': 5.8.0 - '@smithy/node-http-handler': 4.12.1 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/signature-v4-multi-region@3.996.46': - dependencies: - '@aws-sdk/types': 3.974.5 - '@smithy/signature-v4': 5.7.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/token-providers@3.1116.0': - dependencies: - '@aws-sdk/core': 3.977.9 - '@aws-sdk/nested-clients': 3.997.44 - '@aws-sdk/types': 3.974.5 - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/types@3.974.5': - dependencies: - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@aws-sdk/xml-builder@3.972.40': - dependencies: - '@smithy/types': 4.18.0 - tslib: 2.8.1 + chokidar: 5.0.0 + rolldown: 1.2.5 - '@aws/lambda-invoke-store@0.3.0': {} + '@alchemy.run/sigil@0.1.0-alpha.1': {} '@babel/code-frame@7.29.7': dependencies: @@ -4982,121 +4489,148 @@ snapshots: fast-wrap-ansi: 0.2.2 sisteransi: 1.0.5 - '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260704.1)': + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260918.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260704.1 + workerd: 1.20260918.1 - '@cloudflare/workerd-darwin-64@1.20260704.1': + '@cloudflare/workerd-darwin-64@1.20260918.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260704.1': + '@cloudflare/workerd-darwin-arm64@1.20260918.1': optional: true - '@cloudflare/workerd-linux-64@1.20260704.1': + '@cloudflare/workerd-linux-64@1.20260918.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260704.1': + '@cloudflare/workerd-linux-arm64@1.20260918.1': optional: true - '@cloudflare/workerd-windows-64@1.20260704.1': + '@cloudflare/workerd-windows-64@1.20260918.1': optional: true - '@cloudflare/workers-types@5.20260903.1': {} + '@cloudflare/workers-types@5.20261012.1': {} - '@distilled.cloud/aws@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/acme@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@aws-crypto/crc32': 5.2.0 - '@aws-crypto/util': 5.2.0 - '@aws-sdk/credential-providers': 3.1126.0 - '@aws-sdk/types': 3.974.5 - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@smithy/shared-ini-file-loader': 4.7.2 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 + + '@distilled.cloud/aws@1.0.0-rc.13(effect@4.0.3)': + dependencies: + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) '@smithy/types': 4.18.0 - '@smithy/util-base64': 4.6.2 - aws4fetch: 1.0.20 - effect: 4.0.0-rc.112 - fast-xml-parser: 5.11.1 + effect: 4.0.3 + + '@distilled.cloud/axiom@1.0.0-rc.13(effect@4.0.3)': + dependencies: + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 + + '@distilled.cloud/cloudflare@1.0.0-rc.13(effect@4.0.3)': + dependencies: + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 + + '@distilled.cloud/core@1.0.0-rc.13(effect@4.0.3)': + dependencies: + effect: 4.0.3 + graphql: 16.11.0 + + '@distilled.cloud/doppler@1.0.0-rc.13(effect@4.0.3)': + dependencies: + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 + + '@distilled.cloud/fly-io@1.0.0-rc.13(effect@4.0.3)': + dependencies: + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/axiom@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/gcp@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/hetzner@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/core@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/infisical@1.0.0-rc.13(effect@4.0.3)': dependencies: - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/fly-io@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/neon@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/hetzner@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/planetscale@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/neon@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/prisma@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/planetscale@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/railway@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 - '@distilled.cloud/railway@1.0.0-rc.8(effect@4.0.0-rc.112)': + '@distilled.cloud/stripe@1.0.0-rc.13(effect@4.0.3)': dependencies: - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 + + '@distilled.cloud/zerossl@1.0.0-rc.13(effect@4.0.3)': + dependencies: + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + effect: 4.0.3 '@dual-bundle/import-meta-resolve@4.2.1': {} - '@effect/platform-browser@4.0.0-rc.112(effect@4.0.0-rc.112)': + '@effect/platform-browser@4.0.3(effect@4.0.3)': dependencies: - effect: 4.0.0-rc.112 + effect: 4.0.3 - '@effect/platform-node-shared@4.0.0-rc.112(effect@4.0.0-rc.112)': + '@effect/platform-node-shared@4.0.3(effect@4.0.3)': dependencies: - '@types/ws': 8.18.1 - effect: 4.0.0-rc.112 - ws: 8.21.3 + '@types/ws': 8.18.2 + effect: 4.0.3 + ws: 8.22.0 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1)': + '@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1)': dependencies: - '@effect/platform-node-shared': 4.0.0-rc.112(effect@4.0.0-rc.112) - effect: 4.0.0-rc.112 - mime: 4.1.0 + '@effect/platform-node-shared': 4.0.3(effect@4.0.3) + effect: 4.0.3 redis: 6.2.1 - undici: 8.10.1 + undici: 8.11.2 transitivePeerDependencies: - bufferutil - utf-8-validate - '@effect/sql-d1@4.0.0-rc.112(effect@4.0.0-rc.112)': + '@effect/sql-d1@4.0.3(effect@4.0.3)': dependencies: - '@cloudflare/workers-types': 5.20260903.1 - effect: 4.0.0-rc.112 + '@cloudflare/workers-types': 5.20261012.1 + effect: 4.0.3 - '@effect/sql-sqlite-do@4.0.0-rc.112(effect@4.0.0-rc.112)': + '@effect/sql-sqlite-do@4.0.3(effect@4.0.3)': dependencies: - effect: 4.0.0-rc.112 + effect: 4.0.3 - '@effect/vitest@4.0.0-rc.112(effect@4.0.0-rc.112)(vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))': + '@effect/vitest@4.0.3(effect@4.0.3)(vitest@5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))': dependencies: - effect: 4.0.0-rc.112 - vitest: 4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) + effect: 4.0.3 + vitest: 5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) '@electric-sql/pglite-socket@0.0.20(@electric-sql/pglite@0.3.15)': dependencies: @@ -5280,18 +4814,18 @@ snapshots: '@floating-ui/utils@0.2.12': {} - '@foldkit/devtools@0.156.0(@effect/platform-browser@4.0.0-rc.112(effect@4.0.0-rc.112))(@foldkit/ui@0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)))(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112))': + '@foldkit/devtools@0.168.0(@effect/platform-browser@4.0.3(effect@4.0.3))(@foldkit/ui@0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)))(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3))': dependencies: - '@effect/platform-browser': 4.0.0-rc.112(effect@4.0.0-rc.112) - '@foldkit/ui': 0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112)) + '@effect/platform-browser': 4.0.3(effect@4.0.3) + '@foldkit/ui': 0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3)) clsx: 2.1.1 - effect: 4.0.0-rc.112 - foldkit: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + effect: 4.0.3 + foldkit: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) - '@foldkit/markdown@0.9.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0))': + '@foldkit/markdown@0.14.3(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0))': dependencies: - effect: 4.0.0-rc.112 - foldkit: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + effect: 4.0.3 + foldkit: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) remark-directive: 4.0.0 remark-frontmatter: 5.0.0 remark-gfm: 4.0.1 @@ -5301,21 +4835,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@foldkit/ui@0.156.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112))': + '@foldkit/ui@0.168.0(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3))': dependencies: '@floating-ui/dom': 1.8.0 - effect: 4.0.0-rc.112 - foldkit: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) + effect: 4.0.3 + foldkit: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) - '@foldkit/vite-plugin@0.20.0(effect@4.0.0-rc.112)(foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0))': + '@foldkit/vite-plugin@0.27.1(effect@4.0.3)(foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3))(redis@6.2.1)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0))': dependencies: - effect: 4.0.0-rc.112 - foldkit: 0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112) - magic-string: 1.2.3 + '@effect/platform-node': 4.0.3(effect@4.0.3)(redis@6.2.1) + '@effect/platform-node-shared': 4.0.3(effect@4.0.3) + effect: 4.0.3 + foldkit: 0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3) + magic-string: 1.4.3 vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) ws: 8.21.3 transitivePeerDependencies: - bufferutil + - redis - utf-8-validate '@hono/node-server@1.19.9(hono@4.11.4)': @@ -5478,8 +5016,8 @@ snapshots: '@libsql/isomorphic-ws@0.1.5': dependencies: - '@types/ws': 8.18.1 - ws: 8.21.3 + '@types/ws': 8.18.2 + ws: 8.22.0 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -5525,7 +5063,7 @@ snapshots: '@microsoft/api-extractor-model@7.33.11(@types/node@24.13.3)': dependencies: '@microsoft/tsdoc': 0.16.0 - '@microsoft/tsdoc-config': 0.18.1 + '@microsoft/tsdoc-config': 0.18.3 '@rushstack/node-core-library': 5.24.0(@types/node@24.13.3) transitivePeerDependencies: - '@types/node' @@ -5535,14 +5073,14 @@ snapshots: dependencies: '@microsoft/api-extractor-model': 7.33.11(@types/node@24.13.3) '@microsoft/tsdoc': 0.16.0 - '@microsoft/tsdoc-config': 0.18.1 + '@microsoft/tsdoc-config': 0.18.3 '@rushstack/node-core-library': 5.24.0(@types/node@24.13.3) '@rushstack/rig-package': 0.7.3 '@rushstack/terminal': 0.24.3(@types/node@24.13.3) '@rushstack/ts-command-line': 5.3.13(@types/node@24.13.3) diff: 8.0.4 minimatch: 10.2.3 - resolve: 1.22.12 + resolve: 1.22.13 semver: 7.7.4 source-map: 0.6.1 typescript: 5.9.3 @@ -5550,17 +5088,20 @@ snapshots: - '@types/node' optional: true - '@microsoft/tsdoc-config@0.18.1': + '@microsoft/tsdoc-config@0.18.3': dependencies: - '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc': 0.17.1 ajv: 8.18.0 jju: 1.4.0 - resolve: 1.22.12 + resolve: 1.22.13 optional: true '@microsoft/tsdoc@0.16.0': optional: true + '@microsoft/tsdoc@0.17.1': + optional: true + '@modelcontextprotocol/client@2.0.0': dependencies: '@modelcontextprotocol/core': 2.0.0 @@ -5589,24 +5130,6 @@ snapshots: chevrotain: 10.5.0 lilconfig: 2.1.0 - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4': - optional: true - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': - optional: true - '@napi-rs/canvas-android-arm64@1.0.8': optional: true @@ -5660,19 +5183,9 @@ snapshots: '@neon-rs/load@0.0.4': {} - '@nodable/entities@3.0.0': {} - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.3 + '@neon/functions@0.11.0(hono@4.12.14)': + optionalDependencies: + hono: 4.12.14 '@octokit/auth-token@6.0.0': {} @@ -6117,7 +5630,7 @@ snapshots: fs-extra: 11.3.6 import-lazy: 4.0.0 jju: 1.4.0 - resolve: 1.22.12 + resolve: 1.22.13 semver: 7.7.4 optionalDependencies: '@types/node': 24.13.3 @@ -6131,7 +5644,7 @@ snapshots: '@rushstack/rig-package@0.7.3': dependencies: jju: 1.4.0 - resolve: 1.22.12 + resolve: 1.22.13 optional: true '@rushstack/terminal@0.24.3(@types/node@24.13.3)': @@ -6153,68 +5666,10 @@ snapshots: - '@types/node' optional: true - '@smithy/core@3.33.3': - dependencies: - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@smithy/credential-provider-imds@4.5.2': - dependencies: - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@smithy/fetch-http-handler@5.8.0': - dependencies: - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@smithy/is-array-buffer@2.2.0': - dependencies: - tslib: 2.8.1 - - '@smithy/node-config-provider@4.6.2': - dependencies: - '@smithy/core': 3.33.3 - tslib: 2.8.1 - - '@smithy/node-http-handler@4.12.1': - dependencies: - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - - '@smithy/shared-ini-file-loader@4.7.2': - dependencies: - '@smithy/core': 3.33.3 - tslib: 2.8.1 - - '@smithy/signature-v4@5.7.3': - dependencies: - '@smithy/core': 3.33.3 - '@smithy/types': 4.18.0 - tslib: 2.8.1 - '@smithy/types@4.18.0': dependencies: tslib: 2.8.1 - '@smithy/util-base64@4.6.2': - dependencies: - '@smithy/core': 3.33.3 - tslib: 2.8.1 - - '@smithy/util-buffer-from@2.2.0': - dependencies: - '@smithy/is-array-buffer': 2.2.0 - tslib: 2.8.1 - - '@smithy/util-utf8@2.3.0': - dependencies: - '@smithy/util-buffer-from': 2.2.0 - tslib: 2.8.1 - '@standard-schema/spec@1.1.0': {} '@stylexjs/babel-plugin@0.19.0': @@ -6301,7 +5756,7 @@ snapshots: '@types/unist@3.0.3': {} - '@types/ws@8.18.1': + '@types/ws@8.18.2': dependencies: '@types/node': 24.13.3 @@ -6365,57 +5820,30 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@vitest/expect@4.1.11': + '@vitest/mocker@5.0.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))': dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 - chai: 6.2.2 - tinyrainbow: 3.1.1 - - '@vitest/mocker@4.1.11(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))': - dependencies: - '@vitest/spy': 4.1.11 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.3 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.3 optionalDependencies: vite: 8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0) - '@vitest/mocker@4.1.11(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0))': + '@vitest/mocker@5.0.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.11 + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.3 estree-walker: 3.0.3 - magic-string: 0.30.21 + magic-string: 1.4.3 optionalDependencies: vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0) - '@vitest/pretty-format@4.1.11': - dependencies: - tinyrainbow: 3.1.1 - - '@vitest/runner@4.1.11': - dependencies: - '@vitest/utils': 4.1.11 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.1.11': {} - - '@vitest/utils@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.1 + '@vitest/spy@5.0.3': {} acorn@8.18.0: {} + acorn@8.19.0: {} + ajv-draft-04@1.0.0(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -6429,7 +5857,7 @@ snapshots: ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.7 + fast-uri: 3.1.8 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 optional: true @@ -6437,70 +5865,65 @@ snapshots: ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 - fast-uri: 3.1.7 + fast-uri: 3.1.8 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 optional: true - alchemy@2.0.0-beta.76(@alchemy.run/frontend-frameworks@2.0.0-beta.76(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))(vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))(ws@8.21.3): - dependencies: - '@alchemy.run/cloudflare-runtime': 2.0.0-beta.76(@distilled.cloud/cloudflare@1.0.0-rc.8(effect@4.0.0-rc.112))(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(rolldown@1.2.5)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) - '@alchemy.run/floci': 2.0.0-beta.76(effect@4.0.0-rc.112) - '@alchemy.run/node-utils': 2.0.0-beta.76 - '@aws-sdk/credential-providers': 3.1126.0 - '@clack/prompts': 1.7.0 - '@distilled.cloud/aws': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/axiom': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/cloudflare': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/core': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/fly-io': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/hetzner': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/neon': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/planetscale': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@distilled.cloud/railway': 1.0.0-rc.8(effect@4.0.0-rc.112) - '@effect/sql-d1': 4.0.0-rc.112(effect@4.0.0-rc.112) - '@effect/sql-sqlite-do': 4.0.0-rc.112(effect@4.0.0-rc.112) - '@effect/vitest': 4.0.0-rc.112(effect@4.0.0-rc.112)(vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))) + alchemy@2.0.0-beta.80(@alchemy.run/frontend-frameworks@2.0.0-beta.80(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)))(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@effect/vitest@4.0.3(effect@4.0.3)(vitest@5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))))(@types/node@24.13.3)(effect@4.0.3)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))(ws@8.22.0): + dependencies: + '@alchemy.run/cloudflare-runtime': 2.0.0-beta.80(@distilled.cloud/cloudflare@1.0.0-rc.13(effect@4.0.3))(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(rolldown@1.2.5)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) + '@alchemy.run/floci': 2.0.0-beta.80(effect@4.0.3) + '@alchemy.run/node-utils': 2.0.0-beta.80 + '@alchemy.run/sigil': 0.1.0-alpha.1 + '@distilled.cloud/acme': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/aws': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/axiom': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/cloudflare': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/core': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/doppler': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/fly-io': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/gcp': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/hetzner': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/infisical': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/neon': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/planetscale': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/prisma': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/railway': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/stripe': 1.0.0-rc.13(effect@4.0.3) + '@distilled.cloud/zerossl': 1.0.0-rc.13(effect@4.0.3) + '@effect/sql-d1': 4.0.3(effect@4.0.3) + '@effect/sql-sqlite-do': 4.0.3(effect@4.0.3) '@libsql/client': 0.17.4 + '@neon/functions': 0.11.0(hono@4.12.14) '@octokit/rest': 22.0.1 '@octokit/webhooks': 14.2.0 '@prisma/dev': 0.20.0(typescript@7.0.2) - '@smithy/node-config-provider': 4.6.2 - '@smithy/shared-ini-file-loader': 4.7.2 - '@smithy/types': 4.18.0 '@types/aws-lambda': 8.10.163 - aws4fetch: 1.0.20 - capnweb: 0.6.1 - effect: 4.0.0-rc.112 - fast-glob: 3.3.3 - fast-xml-parser: 5.11.1 - ink: 6.8.0(react@19.2.8) - jszip: 3.10.1 + capnweb: 0.12.0 + effect: 4.0.3 + fflate: 0.8.3 libsodium-wrappers: 0.8.4 pathe: 2.0.3 picomatch: 4.0.7 - react: 19.2.8 rolldown: 1.2.5 - undici: 7.29.0 + tinyglobby: 0.2.17 yaml: 2.9.0 optionalDependencies: - '@alchemy.run/frontend-frameworks': 2.0.0-beta.76(@effect/platform-node@4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.0-rc.112)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) - '@effect/platform-node': 4.0.0-rc.112(effect@4.0.0-rc.112)(redis@6.2.1) + '@alchemy.run/frontend-frameworks': 2.0.0-beta.80(@effect/platform-node@4.0.3(effect@4.0.3)(redis@6.2.1))(@types/node@24.13.3)(effect@4.0.3)(hono@4.12.14)(typescript@7.0.2)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) + '@effect/platform-node': 4.0.3(effect@4.0.3)(redis@6.2.1) + '@effect/vitest': 4.0.3(effect@4.0.3)(vitest@5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0))) vite: 8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0) - ws: 8.21.3 + ws: 8.22.0 transitivePeerDependencies: - '@types/node' - '@types/react' - bufferutil + - hono - proxy-agent - react-devtools-core - typescript - utf-8-validate - - vitest - - ansi-escapes@7.3.0: - dependencies: - environment: 1.1.0 ansi-regex@6.3.0: {} @@ -6508,8 +5931,6 @@ snapshots: any-promise@1.3.0: {} - anynum@1.0.1: {} - argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -6517,10 +5938,6 @@ snapshots: assertion-error@2.0.1: {} - auto-bind@5.0.1: {} - - aws4fetch@1.0.20: {} - bail@2.0.2: {} balanced-match@4.0.4: @@ -6530,17 +5947,11 @@ snapshots: before-after-hook@4.0.0: {} - bowser@2.14.1: {} - - brace-expansion@5.0.9: + brace-expansion@5.0.12: dependencies: balanced-match: 4.0.4 optional: true - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - browserslist@4.28.8: dependencies: baseline-browser-mapping: 2.11.20 @@ -6560,18 +5971,16 @@ snapshots: caniuse-lite@1.0.30001810: {} - capnp-es@0.0.14(typescript@7.0.2): + capnp-es@0.0.16(typescript@7.0.2): optionalDependencies: typescript: 7.0.2 - capnweb@0.6.1: {} + capnweb@0.12.0: {} ccount@2.0.1: {} chai@6.2.2: {} - chalk@5.6.2: {} - character-entities-html4@2.1.0: {} character-entities-legacy@3.0.0: {} @@ -6593,16 +6002,9 @@ snapshots: dependencies: readdirp: 4.1.2 - cli-boxes@3.0.0: {} - - cli-cursor@4.0.0: - dependencies: - restore-cursor: 4.0.0 - - cli-truncate@5.2.0: + chokidar@5.0.0: dependencies: - slice-ansi: 8.0.0 - string-width: 8.2.2 + readdirp: 5.1.1 cliui@9.0.1: dependencies: @@ -6614,10 +6016,6 @@ snapshots: cluster-key-slot@1.1.2: {} - code-excerpt@4.0.0: - dependencies: - convert-to-spaces: 2.0.1 - commander@4.1.1: {} confbox@0.1.8: {} @@ -6628,10 +6026,6 @@ snapshots: convert-source-map@2.0.0: {} - convert-to-spaces@2.0.1: {} - - core-util-is@1.0.3: {} - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -6663,10 +6057,7 @@ snapshots: diff@8.0.4: optional: true - effect@4.0.0-rc.112: - dependencies: - fast-check: 4.9.0 - msgpackr: 2.1.0 + effect@4.0.3: {} electron-to-chromium@1.5.420: {} @@ -6674,15 +6065,11 @@ snapshots: entities@8.0.0: {} - environment@1.1.0: {} - es-errors@1.3.0: optional: true es-module-lexer@2.3.2: {} - es-toolkit@1.52.0: {} - esbuild@0.27.7: optionalDependencies: '@esbuild/aix-ppc64': 0.27.7 @@ -6743,8 +6130,6 @@ snapshots: escalade@3.2.0: {} - escape-string-regexp@2.0.0: {} - escape-string-regexp@5.0.0: {} estree-walker@3.0.3: @@ -6761,52 +6146,22 @@ snapshots: extend@3.0.2: {} - fast-check@4.9.0: - dependencies: - pure-rand: 8.4.2 - fast-deep-equal@3.1.3: optional: true - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-string-truncated-width@3.0.3: {} fast-string-width@3.0.2: dependencies: fast-string-truncated-width: 3.0.3 - fast-uri@3.1.7: + fast-uri@3.1.8: optional: true fast-wrap-ansi@0.2.2: dependencies: fast-string-width: 3.0.2 - fast-xml-builder@1.3.1: - dependencies: - path-expression-matcher: 1.6.2 - xml-naming: 0.3.0 - - fast-xml-parser@5.11.1: - dependencies: - '@nodable/entities': 3.0.0 - fast-xml-builder: 1.3.1 - is-unsafe: 2.0.2 - path-expression-matcher: 1.6.2 - strnum: 2.4.2 - xml-naming: 0.3.0 - - fastq@1.20.3: - dependencies: - reusify: 1.1.0 - fault@2.0.1: dependencies: format: 0.2.2 @@ -6815,9 +6170,7 @@ snapshots: optionalDependencies: picomatch: 4.0.7 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 + fflate@0.8.3: {} fix-dts-default-cjs-exports@1.0.1: dependencies: @@ -6825,9 +6178,10 @@ snapshots: mlly: 1.8.2 rollup: 4.63.1 - foldkit@0.156.0(patch_hash=2e3fbfd91eeb160fa0879eebb47a2962a50160152acfa4f47e95626db473be57)(effect@4.0.0-rc.112): + foldkit@0.168.0(patch_hash=b8953975f3bc58aaa77313d70f7cfb9508aa7e47ed999849f1ca98b2cfeeef98)(@effect/platform-browser@4.0.3(effect@4.0.3))(effect@4.0.3): dependencies: - effect: 4.0.0-rc.112 + '@effect/platform-browser': 4.0.3(effect@4.0.3) + effect: 4.0.3 parse5: 8.0.1 foreground-child@3.3.1: @@ -6861,16 +6215,14 @@ snapshots: get-port-please@3.2.0: {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - graceful-fs@4.2.11: {} grammex@3.1.13: {} graphmatch@1.1.1: {} + graphql@16.11.0: {} + has-flag@4.0.0: optional: true @@ -6887,49 +6239,11 @@ snapshots: human-id@4.2.1: {} - immediate@3.0.6: {} - import-lazy@4.0.0: optional: true import-meta-resolve@4.2.0: {} - indent-string@5.0.0: {} - - inherits@2.0.4: {} - - ink@6.8.0(react@19.2.8): - dependencies: - '@alcalzone/ansi-tokenize': 0.2.5 - ansi-escapes: 7.3.0 - ansi-styles: 6.2.3 - auto-bind: 5.0.1 - chalk: 5.6.2 - cli-boxes: 3.0.0 - cli-cursor: 4.0.0 - cli-truncate: 5.2.0 - code-excerpt: 4.0.0 - es-toolkit: 1.52.0 - indent-string: 5.0.0 - is-in-ci: 2.0.0 - patch-console: 2.0.0 - react: 19.2.8 - react-reconciler: 0.33.0(react@19.2.8) - scheduler: 0.27.0 - signal-exit: 3.0.7 - slice-ansi: 8.0.0 - stack-utils: 2.0.6 - string-width: 8.2.2 - terminal-size: 4.0.1 - type-fest: 5.9.0 - widest-line: 6.0.0 - wrap-ansi: 9.0.2 - ws: 8.21.3 - yoga-layout: 3.2.1 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - inter-font@3.19.0: {} invariant@2.2.4: @@ -6943,35 +6257,17 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - is-core-module@2.16.2: + is-core-module@2.17.0: dependencies: hasown: 2.0.4 optional: true is-decimal@2.0.1: {} - is-extglob@2.1.1: {} - - is-fullwidth-code-point@5.1.0: - dependencies: - get-east-asian-width: 1.6.0 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - is-hexadecimal@2.0.1: {} - is-in-ci@2.0.0: {} - - is-number@7.0.0: {} - is-plain-obj@4.1.0: {} - is-unsafe@2.0.2: {} - - isarray@1.0.0: {} - isexe@2.0.0: {} jju@1.4.0: {} @@ -7002,13 +6298,6 @@ snapshots: graceful-fs: 4.2.11 optional: true - jszip@3.10.1: - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - launch-editor@2.14.1: dependencies: picocolors: 1.1.1 @@ -7035,10 +6324,6 @@ snapshots: '@libsql/linux-x64-musl': 0.5.29 '@libsql/win32-x64-msvc': 0.5.29 - lie@3.3.0: - dependencies: - immediate: 3.0.6 - lightningcss-android-arm64@1.33.0: optional: true @@ -7112,7 +6397,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 - magic-string@1.2.3: + magic-string@1.4.3: dependencies: '@jridgewell/sourcemap-codec': 1.6.0 @@ -7245,8 +6530,6 @@ snapshots: dependencies: '@types/mdast': 4.0.4 - merge2@1.4.1: {} - micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 @@ -7455,18 +6738,11 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.2 - - mime@4.1.0: {} - - mimic-fn@2.1.0: {} + mime@4.1.1: {} minimatch@10.2.3: dependencies: - brace-expansion: 5.0.9 + brace-expansion: 5.0.12 optional: true mlly@1.8.2: @@ -7480,22 +6756,6 @@ snapshots: ms@2.1.3: {} - msgpackr-extract@3.0.4: - dependencies: - node-gyp-build-optional-packages: 5.2.2 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.4 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.4 - optional: true - - msgpackr@2.1.0: - optionalDependencies: - msgpackr-extract: 3.0.4 - mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -7504,21 +6764,12 @@ snapshots: nanoid@3.3.18: {} - node-gyp-build-optional-packages@5.2.2: - dependencies: - detect-libc: 2.1.2 - optional: true - node-releases@2.0.54: {} object-assign@4.1.1: {} obug@2.1.4: {} - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - oxfmt@0.44.0: dependencies: tinypool: 2.1.0 @@ -7583,10 +6834,6 @@ snapshots: dependencies: entities: 8.0.0 - patch-console@2.0.0: {} - - path-expression-matcher@1.6.2: {} - path-key@3.1.1: {} path-parse@1.0.7: @@ -7609,8 +6856,6 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.7: {} pirates@4.0.7: {} @@ -7646,8 +6891,6 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - process-nextick-args@2.0.1: {} - promise-limit@2.7.0: {} proper-lockfile@4.1.2: @@ -7656,29 +6899,13 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 - pure-rand@8.4.2: {} - - queue-microtask@1.2.3: {} - - react-reconciler@0.33.0(react@19.2.8): - dependencies: - react: 19.2.8 - scheduler: 0.27.0 - - react@19.2.8: {} - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 + react@19.2.8: + optional: true readdirp@4.1.2: {} + readdirp@5.1.1: {} + redis@6.2.1: dependencies: '@redis/bloom': 6.2.1(@redis/client@6.2.1) @@ -7743,23 +6970,16 @@ snapshots: resolve-from@5.0.0: {} - resolve@1.22.12: + resolve@1.22.13: dependencies: es-errors: 1.3.0 - is-core-module: 2.16.2 + is-core-module: 2.17.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 optional: true - restore-cursor@4.0.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - retry@0.12.0: {} - reusify@1.1.0: {} - rolldown@1.2.5: dependencies: '@oxc-project/types': 0.146.0 @@ -7834,14 +7054,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.63.1 fsevents: 2.3.3 - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - safe-buffer@5.1.2: {} - - scheduler@0.27.0: {} - semver@6.3.1: {} semver@7.7.4: @@ -7849,8 +7061,6 @@ snapshots: semver@7.8.5: {} - setimmediate@1.0.5: {} - sharp@0.35.4(@types/node@24.13.3): dependencies: '@img/colour': 1.1.0 @@ -7892,19 +7102,12 @@ snapshots: shell-quote@1.10.0: {} - siginfo@2.0.0: {} - signal-exit@3.0.7: {} signal-exit@4.1.0: {} sisteransi@1.0.5: {} - slice-ansi@8.0.0: - dependencies: - ansi-styles: 6.2.3 - is-fullwidth-code-point: 5.1.0 - source-map-js@1.2.1: {} source-map@0.6.1: @@ -7915,11 +7118,7 @@ snapshots: sprintf-js@1.0.3: optional: true - stack-utils@2.0.6: - dependencies: - escape-string-regexp: 2.0.0 - - stackback@0.0.2: {} + srvx@1.0.5: {} std-env@3.10.0: {} @@ -7939,10 +7138,6 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - stringify-entities@4.0.4: dependencies: character-entities-html4: 2.1.0 @@ -7952,10 +7147,6 @@ snapshots: dependencies: ansi-regex: 6.3.0 - strnum@2.4.2: - dependencies: - anynum: 1.0.1 - styleq@0.2.1: {} sucrase@3.35.1: @@ -7976,10 +7167,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: optional: true - tagged-tag@1.0.0: {} - - terminal-size@4.0.1: {} - thenify-all@1.6.0: dependencies: thenify: 3.3.1 @@ -7988,7 +7175,7 @@ snapshots: dependencies: any-promise: 1.3.0 - tinybench@2.9.0: {} + tinybench@6.2.2: {} tinyexec@0.3.2: {} @@ -8001,12 +7188,6 @@ snapshots: tinypool@2.1.0: {} - tinyrainbow@3.1.1: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - tree-kill@1.2.2: {} trough@2.2.0: {} @@ -8055,10 +7236,6 @@ snapshots: '@turbo/windows-64': 2.11.3 '@turbo/windows-arm64': 2.11.3 - type-fest@5.9.0: - dependencies: - tagged-tag: 1.0.0 - typescript@5.9.3: {} typescript@7.0.2: @@ -8088,9 +7265,7 @@ snapshots: undici-types@7.18.2: {} - undici@7.29.0: {} - - undici@8.10.1: {} + undici@8.11.2: {} unenv@2.0.0-rc.24: dependencies: @@ -8133,7 +7308,7 @@ snapshots: unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 - acorn: 8.18.0 + acorn: 8.19.0 picomatch: 4.0.7 webpack-virtual-modules: 0.6.2 @@ -8143,8 +7318,6 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - util-deprecate@1.0.2: {} - valibot@1.2.0(typescript@7.0.2): optionalDependencies: typescript: 7.0.2 @@ -8185,55 +7358,47 @@ snapshots: fsevents: 2.3.3 yaml: 2.9.0 - vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)): + optionalDependencies: + vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0) + + vitest@5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)): dependencies: - '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.11 - '@vitest/runner': 4.1.11 - '@vitest/snapshot': 4.1.11 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0)) + chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 0.30.21 + magic-string: 1.4.3 obug: 2.1.4 - pathe: 2.0.3 picomatch: 4.0.7 std-env: 4.2.0 - tinybench: 2.9.0 + tinybench: 6.2.2 tinyexec: 1.3.0 tinyglobby: 0.2.17 - tinyrainbow: 3.1.1 vite: 8.2.2(@types/node@24.13.3)(esbuild@0.27.7)(yaml@2.9.0) - why-is-node-running: 2.3.0 + why-is-node-running: 3.2.1 optionalDependencies: '@types/node': 24.13.3 transitivePeerDependencies: - msw - vitest@4.1.11(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)): + vitest@5.0.3(@types/node@24.13.3)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)): dependencies: - '@vitest/expect': 4.1.11 - '@vitest/mocker': 4.1.11(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.11 - '@vitest/runner': 4.1.11 - '@vitest/snapshot': 4.1.11 - '@vitest/spy': 4.1.11 - '@vitest/utils': 4.1.11 + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.3(vite@8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0)) + chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 - magic-string: 0.30.21 + magic-string: 1.4.3 obug: 2.1.4 - pathe: 2.0.3 picomatch: 4.0.7 std-env: 4.2.0 - tinybench: 2.9.0 + tinybench: 6.2.2 tinyexec: 1.3.0 tinyglobby: 0.2.17 - tinyrainbow: 3.1.1 vite: 8.2.2(@types/node@24.13.3)(esbuild@0.28.2)(yaml@2.9.0) - why-is-node-running: 2.3.0 + why-is-node-running: 3.2.1 optionalDependencies: '@types/node': 24.13.3 transitivePeerDependencies: @@ -8245,22 +7410,15 @@ snapshots: dependencies: isexe: 2.0.0 - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 + why-is-node-running@3.2.1: {} - widest-line@6.0.0: - dependencies: - string-width: 8.2.2 - - workerd@1.20260704.1: + workerd@1.20260918.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260704.1 - '@cloudflare/workerd-darwin-arm64': 1.20260704.1 - '@cloudflare/workerd-linux-64': 1.20260704.1 - '@cloudflare/workerd-linux-arm64': 1.20260704.1 - '@cloudflare/workerd-windows-64': 1.20260704.1 + '@cloudflare/workerd-darwin-64': 1.20260918.1 + '@cloudflare/workerd-darwin-arm64': 1.20260918.1 + '@cloudflare/workerd-linux-64': 1.20260918.1 + '@cloudflare/workerd-linux-arm64': 1.20260918.1 + '@cloudflare/workerd-windows-64': 1.20260918.1 wrap-ansi@9.0.2: dependencies: @@ -8270,7 +7428,7 @@ snapshots: ws@8.21.3: {} - xml-naming@0.3.0: {} + ws@8.22.0: {} y18n@5.0.8: {} @@ -8293,8 +7451,6 @@ snapshots: dependencies: pend: 1.2.0 - yoga-layout@3.2.1: {} - zeptomatch@2.1.0: dependencies: grammex: 3.1.13 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4369db9..4db5b29 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,35 +5,57 @@ packages: - packages/packages/* catalog: + "@effect/platform-browser": 4.0.3 + "@effect/platform-node": 4.0.3 "@modelcontextprotocol/ext-apps": 2.0.0 "@playwright/test": 1.62.1 - "@foldkit/devtools": 0.156.0 - "@foldkit/markdown": 0.9.0 - "@foldkit/ui": 0.156.0 - "@foldkit/vite-plugin": 0.20.0 + "@foldkit/devtools": 0.168.0 + "@foldkit/markdown": 0.14.3 + "@foldkit/ui": 0.168.0 + "@foldkit/vite-plugin": 0.27.1 "@lucide/icons": 1.40.0 "@stylexjs/stylex": 0.19.0 "@stylexjs/unplugin": 0.19.0 "@types/node": 24.13.3 dejavu-fonts-ttf: 2.37.3 - effect: 4.0.0-rc.112 - foldkit: 0.156.0 + effect: 4.0.3 + foldkit: 0.168.0 inter-font: 3.19.0 playwright: 1.62.1 pdf-lib: 1.17.1 pdfjs-dist: 6.3.289 typescript: 7.0.2 vite: 8.2.2 - vitest: 4.1.11 + vitest: 5.0.3 catalogs: peers: - "@foldkit/ui": ^0.156.0 + "@foldkit/ui": ^0.168.0 "@stylexjs/stylex": ^0.19.0 - effect: ^4.0.0-rc.112 - foldkit: ^0.156.0 + effect: ^4.0.3 + foldkit: ^0.168.0 engineStrict: true patchedDependencies: - foldkit@0.156.0: patches/foldkit@0.156.0.patch + foldkit@0.168.0: patches/foldkit@0.168.0.patch + +# Foldkit's released peers are pinned to 4.0.0. Only these releases may use +# the tested 4.0.3 patch; all other peer requirements are still checked. +peerDependencyRules: + allowedVersions: + "foldkit@0.168.0>effect": 4.0.3 + "foldkit@0.168.0>@effect/platform-browser": 4.0.3 + "@foldkit/ui@0.168.0>effect": 4.0.3 + "@foldkit/devtools@0.168.0>effect": 4.0.3 + "@foldkit/devtools@0.168.0>@effect/platform-browser": 4.0.3 + "@foldkit/markdown@0.14.3>effect": 4.0.3 + "@foldkit/vite-plugin@0.27.1>effect": 4.0.3 + +# The Vite plugin also pins its server dependencies to 4.0.0. +overrides: + "@foldkit/vite-plugin@0.27.1>@effect/platform-node": 4.0.3 + "@foldkit/vite-plugin@0.27.1>@effect/platform-node-shared": 4.0.3 + # Alchemy accepts stable Effect; pin the existing SQL/test companions too. + "alchemy@2.0.0-beta.80>@effect/sql-d1": 4.0.3 + "alchemy@2.0.0-beta.80>@effect/sql-sqlite-do": 4.0.3 diff --git a/scripts/attribute-repro.mjs b/scripts/attribute-repro.mjs index 026f44f..4ee94ba 100644 --- a/scripts/attribute-repro.mjs +++ b/scripts/attribute-repro.mjs @@ -16,7 +16,7 @@ // that mention M are generic. // split-intersect `split`, with the generic variants left as intersections. // structural Attribute is one covariant interface every variant is -// assignable to (the patch in patches/foldkit@0.156.0.patch). +// assignable to (the patch in patches/foldkit@0.168.0.patch). // opaque Attribute is a single branded interface (for comparison). // explicit Same as `enum`, but every helper call passes `` explicitly. import { mkdirSync, writeFileSync } from "node:fs"; diff --git a/scripts/check-packages.mjs b/scripts/check-packages.mjs index bc627dd..0622314 100644 --- a/scripts/check-packages.mjs +++ b/scripts/check-packages.mjs @@ -6,10 +6,10 @@ import { join, resolve } from "node:path"; const repositoryRoot = resolve(import.meta.dirname, ".."); const packagesRoot = join(repositoryRoot, "packages"); const sharedRuntimeVersions = { - "@foldkit/ui": "0.156.0", + "@foldkit/ui": "0.168.0", "@stylexjs/stylex": "0.19.0", - effect: "4.0.0-rc.112", - foldkit: "0.156.0", + effect: "4.0.3", + foldkit: "0.168.0", }; const run = (command, args, options = {}) => @@ -166,13 +166,23 @@ try { scripts: { build: "vite build", test: "vitest run", typecheck: "tsc --noEmit" }, dependencies: Object.fromEntries([ ...Object.entries(sharedRuntimeVersions), + ["@effect/platform-browser", "4.0.3"], ...[...tarballs].map(([name, archivePath]) => [name, `file:${archivePath}`]), ]), + // Mirror the workspace's exception for these exact Foldkit releases. + // npm overrides also apply to peers; no global peer bypass is used. + overrides: { + "foldkit@0.168.0": { + effect: "$effect", + "@effect/platform-browser": "$@effect/platform-browser", + }, + "@foldkit/ui@0.168.0": { effect: "$effect" }, + }, devDependencies: { "@stylexjs/unplugin": "0.19.0", typescript: "6.0.3", vite: "8.2.2", - vitest: "4.1.11", + vitest: "5.0.3", }, }, null, diff --git a/scripts/infra.test.ts b/scripts/infra.test.ts new file mode 100644 index 0000000..054bf8b --- /dev/null +++ b/scripts/infra.test.ts @@ -0,0 +1,13 @@ +import * as Alchemy from "alchemy"; +import * as Cloudflare from "alchemy/Cloudflare"; +import { Effect } from "effect"; +import { effect, expect } from "@effect/vitest"; + +effect("loads the deployment APIs with the shared Effect runtime", () => + Effect.sync(() => { + expect(Alchemy.Stack).toBeTypeOf("function"); + expect(Cloudflare.Website.Foldkit).toBeTypeOf("function"); + expect(Cloudflare.providers).toBeTypeOf("function"); + expect(Cloudflare.state).toBeTypeOf("function"); + }), +); diff --git a/tsconfig.json b/tsconfig.json index 4fcaa54..cc6a945 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,5 +9,5 @@ "skipLibCheck": true, "types": ["node"] }, - "include": ["alchemy.run.ts"] + "include": ["alchemy.run.ts", "scripts/infra.test.ts", "vitest.infra.config.ts"] } diff --git a/vitest.infra.config.ts b/vitest.infra.config.ts new file mode 100644 index 0000000..e52f3ab --- /dev/null +++ b/vitest.infra.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ test: { include: ["scripts/infra.test.ts"] } }); From 629e5de667af9e46e06e869dc66f33ab36eb4f57 Mon Sep 17 00:00:00 2001 From: Ben Jacobson Date: Mon, 12 Oct 2026 20:38:54 +0000 Subject: [PATCH 2/2] Document the carried Foldkit patch and custom-event migration --- docs/foldkit-upstream.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/foldkit-upstream.md b/docs/foldkit-upstream.md index 28f0861..cdc9133 100644 --- a/docs/foldkit-upstream.md +++ b/docs/foldkit-upstream.md @@ -13,8 +13,9 @@ Status values: **carried** (we ship a local patch or workaround), **proposed** ## `Attribute` type-check blow-up -- Status: **carried** as `patches/foldkit@0.156.0.patch` (bjacobso/foldworks#40). - Not filed. +- Status: **carried** as `patches/foldkit@0.168.0.patch` (bjacobso/foldworks#40). + Rebased onto `foldkit@0.168.0`; its published declaration remains a union. + The patch still changes declarations only. Not filed. - Affects: every generic `view(config, h)` component and helper. In practice that is `@foldworks/ui`, and any package built the same way. - Guard: `packages/ui/src/foldkit-attribute.test.ts` fails to type-check if @@ -138,9 +139,9 @@ queue and a fiber. through its `define` API, not for a plain `div`. This literal already breaks on upstream `main`. Foldkit #913 changed the -variant to `f: (event: CustomEvent) => Option`, with the detail -decoded against a Schema, so the next Foldkit upgrade must touch this code -anyway. +variant to `f: (event: CustomEvent) => Option`. Foldworks +now validates the detail against its Message schema and returns `Some` only +for valid messages, preserving synchronous dispatch on `foldkit@0.168.0`. Ask: a public `h.OnCustomEvent(name, detailSchema, toMessage)` (or similar) for plain elements, with synchronous dispatch.