@swirls/looms 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/actor/host.js +2 -1
- package/dist/agent/debugger/agent-chat.d.ts +2 -0
- package/dist/agent/debugger/agent-chat.js +176 -0
- package/dist/agent/debugger/index.d.ts +5 -0
- package/dist/agent/debugger/index.js +19 -0
- package/dist/agent/debugger/summarize.d.ts +2 -0
- package/dist/agent/debugger/summarize.js +51 -0
- package/dist/agent/debugger/token-usage.d.ts +3 -0
- package/dist/agent/debugger/token-usage.js +11 -0
- package/dist/agent/definitions.d.ts +3 -11
- package/dist/agent/definitions.js +5 -6
- package/dist/agent/signals.d.ts +2 -0
- package/dist/agent/signals.js +18 -16
- package/dist/agent/tool-schema.d.ts +1 -17
- package/dist/agent/tool-schema.js +3 -42
- package/dist/ai-vercel/index.d.ts +1 -1
- package/dist/ai-vercel/vercel-evaluator.d.ts +21 -8
- package/dist/ai-vercel/vercel-evaluator.js +7 -2
- package/dist/approval/debugger/approvals-view.d.ts +3 -0
- package/dist/approval/debugger/approvals-view.js +16 -0
- package/dist/approval/debugger/index.d.ts +4 -0
- package/dist/approval/debugger/index.js +11 -0
- package/dist/approval/debugger/summarize.d.ts +2 -0
- package/dist/approval/debugger/summarize.js +17 -0
- package/dist/client/client.d.ts +18 -0
- package/dist/client/client.js +3 -1
- package/dist/cloudflare/router.d.ts +1 -1
- package/dist/cloudflare/router.js +2 -2
- package/dist/core/compose.js +1 -0
- package/dist/core/debugger-host.d.ts +16 -0
- package/dist/core/debugger-host.js +1 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +1 -0
- package/dist/core/module.d.ts +2 -0
- package/dist/core/published.d.ts +43 -0
- package/dist/core/published.js +44 -0
- package/dist/core/schema.d.ts +3 -1
- package/dist/core/schema.js +38 -0
- package/dist/core/snapshot-store.d.ts +4 -0
- package/dist/core/snapshot-store.js +7 -0
- package/dist/core/sqlite-store.js +39 -0
- package/dist/debugger/app/assets/index-B3Ah8WAE.css +2 -0
- package/dist/debugger/app/assets/index-Qb371t_O.js +22 -0
- package/dist/debugger/app/assets/rolldown-runtime-CbXtAM7H.js +1 -0
- package/dist/debugger/app/assets/routes-CjtEHgPQ.js +47 -0
- package/dist/debugger/app/index.html +14 -0
- package/dist/debugger/components/composer.d.ts +17 -0
- package/dist/debugger/components/composer.js +25 -0
- package/dist/debugger/components/debugger-split.d.ts +2 -1
- package/dist/debugger/components/debugger-split.js +2 -2
- package/dist/debugger/components/event-inspector.d.ts +2 -1
- package/dist/debugger/components/event-inspector.js +25 -16
- package/dist/debugger/components/event-stream.d.ts +2 -1
- package/dist/debugger/components/event-stream.js +12 -71
- package/dist/debugger/components/follow-list.d.ts +19 -0
- package/dist/debugger/components/follow-list.js +96 -0
- package/dist/debugger/components/json-tree.d.ts +11 -0
- package/dist/debugger/components/json-tree.js +120 -0
- package/dist/debugger/components/json-view.js +2 -2
- package/dist/debugger/components/run-tree.js +53 -20
- package/dist/debugger/components/status-dot.d.ts +4 -0
- package/dist/debugger/components/status-dot.js +6 -2
- package/dist/debugger/context.d.ts +14 -0
- package/dist/debugger/context.js +17 -0
- package/dist/debugger/contracts.d.ts +7 -4
- package/dist/debugger/events/builtin-families.d.ts +3 -0
- package/dist/debugger/events/builtin-families.js +9 -0
- package/dist/debugger/events/catalog.d.ts +3 -4
- package/dist/debugger/events/catalog.js +27 -17
- package/dist/debugger/events/protocol-summarize.d.ts +1 -2
- package/dist/debugger/events/protocol-summarize.js +29 -37
- package/dist/debugger/index.d.ts +20 -6
- package/dist/debugger/index.js +19 -5
- package/dist/debugger/lib/cn.d.ts +1 -0
- package/dist/debugger/lib/cn.js +3 -0
- package/dist/debugger/lib/event-meta.d.ts +2 -3
- package/dist/debugger/lib/event-meta.js +6 -11
- package/dist/debugger/lib/json.d.ts +25 -0
- package/dist/debugger/lib/json.js +87 -0
- package/dist/debugger/lib/payload.d.ts +7 -0
- package/dist/debugger/lib/payload.js +11 -0
- package/dist/debugger/lib/time.d.ts +3 -0
- package/dist/debugger/lib/time.js +24 -0
- package/dist/debugger/plugin.d.ts +36 -0
- package/dist/debugger/plugin.js +14 -0
- package/dist/debugger/server/index.d.ts +14 -0
- package/dist/debugger/server/index.js +114 -0
- package/dist/debugger/shell/debugger-shell.d.ts +15 -0
- package/dist/debugger/shell/debugger-shell.js +43 -0
- package/dist/debugger/shell/definition-key.d.ts +3 -0
- package/dist/debugger/shell/definition-key.js +4 -0
- package/dist/debugger/shell/events-pane.d.ts +12 -0
- package/dist/debugger/shell/events-pane.js +25 -0
- package/dist/debugger/shell/projections-panel.d.ts +4 -0
- package/dist/debugger/shell/projections-panel.js +59 -0
- package/dist/debugger/shell/run-controls.d.ts +4 -0
- package/dist/debugger/shell/run-controls.js +72 -0
- package/dist/debugger/shell/run-search.d.ts +6 -0
- package/dist/debugger/shell/run-search.js +102 -0
- package/dist/debugger/shell/run-type-menu.d.ts +6 -0
- package/dist/debugger/shell/run-type-menu.js +11 -0
- package/dist/debugger/shell/schema-form.d.ts +31 -0
- package/dist/debugger/shell/schema-form.js +117 -0
- package/dist/debugger/shell/start-form.d.ts +3 -0
- package/dist/debugger/shell/start-form.js +75 -0
- package/dist/debugger/shell/use-start-run.d.ts +14 -0
- package/dist/debugger/shell/use-start-run.js +32 -0
- package/dist/debugger/shell/workspace.d.ts +6 -0
- package/dist/debugger/shell/workspace.js +15 -0
- package/dist/debugger/styles.css +111 -27
- package/dist/debugger/ui/button.d.ts +24 -0
- package/dist/debugger/ui/button.js +24 -0
- package/dist/debugger/ui/command.d.ts +9 -0
- package/dist/debugger/ui/command.js +24 -0
- package/dist/debugger/ui/dropdown-menu.d.ts +13 -0
- package/dist/debugger/ui/dropdown-menu.js +37 -0
- package/dist/debugger/ui/panel.d.ts +14 -0
- package/dist/debugger/ui/panel.js +16 -0
- package/dist/debugger/ui/popover.d.ts +7 -0
- package/dist/debugger/ui/popover.js +17 -0
- package/dist/debugger/ui/resizable.d.ts +7 -0
- package/dist/debugger/ui/resizable.js +14 -0
- package/dist/debugger/ui/scroll-area.d.ts +2 -1
- package/dist/debugger/ui/scroll-area.js +5 -3
- package/dist/debugger/ui/select.d.ts +10 -0
- package/dist/debugger/ui/select.js +21 -0
- package/dist/debugger/ui/tabs.js +2 -2
- package/dist/debugger/ui/textarea.d.ts +3 -0
- package/dist/debugger/ui/textarea.js +7 -0
- package/dist/react/store.d.ts +1 -1
- package/dist/react/store.js +1 -1
- package/dist/runtime/looms.d.ts +9 -1
- package/dist/runtime/looms.js +4 -2
- package/dist/runtime/repository.js +16 -1
- package/dist/runtime/runtime.js +16 -1
- package/dist/runtime/server/auth.d.ts +1 -0
- package/dist/runtime/server/auth.js +5 -0
- package/dist/runtime/server/debugger-host.d.ts +11 -0
- package/dist/runtime/server/debugger-host.js +32 -0
- package/dist/runtime/server/fetch-handler.d.ts +5 -1
- package/dist/runtime/server/fetch-handler.js +24 -13
- package/dist/runtime/server/http.d.ts +1 -0
- package/dist/runtime/server/http.js +4 -0
- package/dist/runtime/server/routes/definitions.d.ts +3 -0
- package/dist/runtime/server/routes/definitions.js +20 -0
- package/dist/runtime/server/routes/runs.js +8 -1
- package/dist/runtime/server.d.ts +3 -1
- package/dist/runtime/server.js +3 -1
- package/dist/runtime/types.d.ts +3 -1
- package/dist/s2/snapshot-store.js +43 -1
- package/dist/workflow/debugger/index.d.ts +4 -0
- package/dist/workflow/debugger/index.js +11 -0
- package/dist/workflow/debugger/nodes-view.d.ts +3 -0
- package/dist/workflow/debugger/nodes-view.js +13 -0
- package/dist/workflow/debugger/summarize.d.ts +2 -0
- package/dist/workflow/debugger/summarize.js +30 -0
- package/package.json +20 -1
- package/dist/debugger/events/domain-summarize.d.ts +0 -3
- package/dist/debugger/events/domain-summarize.js +0 -107
- package/dist/debugger/events/family.d.ts +0 -8
- package/dist/debugger/events/family.js +0 -44
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Command as CommandPrimitive } from 'cmdk';
|
|
3
|
+
import { Search } from 'lucide-react';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
function Command({ className, ...props }) {
|
|
7
|
+
return (_jsx(CommandPrimitive, { "data-slot": "command", className: cn('flex size-full flex-col overflow-hidden', className), ...props }));
|
|
8
|
+
}
|
|
9
|
+
function CommandInput({ className, ...props }) {
|
|
10
|
+
return (_jsxs("div", { "data-slot": "command-input-wrapper", className: "border-border flex h-10 items-center gap-2 border-b px-3", children: [_jsx(Search, { className: "text-muted-foreground size-3.5 shrink-0", "aria-hidden": true }), _jsx(CommandPrimitive.Input, { "data-slot": "command-input", className: cn('placeholder:text-muted-foreground flex h-full w-full bg-transparent text-[13px] outline-none disabled:cursor-not-allowed disabled:opacity-50', className), ...props })] }));
|
|
11
|
+
}
|
|
12
|
+
function CommandList({ className, ...props }) {
|
|
13
|
+
return (_jsx(CommandPrimitive.List, { "data-slot": "command-list", className: cn('max-h-80 scroll-py-1 overflow-x-hidden overflow-y-auto overscroll-contain p-1', className), ...props }));
|
|
14
|
+
}
|
|
15
|
+
function CommandEmpty({ className, ...props }) {
|
|
16
|
+
return (_jsx(CommandPrimitive.Empty, { "data-slot": "command-empty", className: cn('text-muted-foreground py-8 text-center text-xs', className), ...props }));
|
|
17
|
+
}
|
|
18
|
+
function CommandGroup({ className, ...props }) {
|
|
19
|
+
return (_jsx(CommandPrimitive.Group, { "data-slot": "command-group", className: cn('[&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:pt-2 [&_[cmdk-group-heading]]:pb-1 [&_[cmdk-group-heading]]:text-[11px] [&_[cmdk-group-heading]]:font-medium', className), ...props }));
|
|
20
|
+
}
|
|
21
|
+
function CommandItem({ className, ...props }) {
|
|
22
|
+
return (_jsx(CommandPrimitive.Item, { "data-slot": "command-item", className: cn('data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-xs outline-none select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50', className), ...props }));
|
|
23
|
+
}
|
|
24
|
+
export { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
declare function DropdownMenu(props: React.ComponentProps<typeof DropdownMenuPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function DropdownMenuTrigger(props: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>): React.JSX.Element;
|
|
5
|
+
declare function DropdownMenuContent({ className, sideOffset, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>): React.JSX.Element;
|
|
6
|
+
declare function DropdownMenuGroup(props: React.ComponentProps<typeof DropdownMenuPrimitive.Group>): React.JSX.Element;
|
|
7
|
+
declare function DropdownMenuLabel({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Label>): React.JSX.Element;
|
|
8
|
+
declare function DropdownMenuItem({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Item>): React.JSX.Element;
|
|
9
|
+
declare function DropdownMenuCheckboxItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>): React.JSX.Element;
|
|
10
|
+
declare function DropdownMenuRadioGroup(props: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>): React.JSX.Element;
|
|
11
|
+
declare function DropdownMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>): React.JSX.Element;
|
|
12
|
+
declare function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>): React.JSX.Element;
|
|
13
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Check } from 'lucide-react';
|
|
3
|
+
import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
const itemClass = 'focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-xs outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:size-3.5 [&_svg]:shrink-0';
|
|
7
|
+
function DropdownMenu(props) {
|
|
8
|
+
return _jsx(DropdownMenuPrimitive.Root, { "data-slot": "dropdown-menu", ...props });
|
|
9
|
+
}
|
|
10
|
+
function DropdownMenuTrigger(props) {
|
|
11
|
+
return _jsx(DropdownMenuPrimitive.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
12
|
+
}
|
|
13
|
+
function DropdownMenuContent({ className, sideOffset = 6, ...props }) {
|
|
14
|
+
return (_jsx(DropdownMenuPrimitive.Portal, { children: _jsx(DropdownMenuPrimitive.Content, { "data-slot": "dropdown-menu-content", sideOffset: sideOffset, className: cn('debugger-pop bg-card text-card-foreground border-border z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-40 overflow-x-hidden overflow-y-auto overscroll-contain rounded-lg border p-1 shadow-xl shadow-black/30', className), ...props }) }));
|
|
15
|
+
}
|
|
16
|
+
function DropdownMenuGroup(props) {
|
|
17
|
+
return _jsx(DropdownMenuPrimitive.Group, { "data-slot": "dropdown-menu-group", ...props });
|
|
18
|
+
}
|
|
19
|
+
function DropdownMenuLabel({ className, ...props }) {
|
|
20
|
+
return (_jsx(DropdownMenuPrimitive.Label, { "data-slot": "dropdown-menu-label", className: cn('text-muted-foreground px-2 pt-2 pb-1 text-[11px] font-medium', className), ...props }));
|
|
21
|
+
}
|
|
22
|
+
function DropdownMenuItem({ className, ...props }) {
|
|
23
|
+
return (_jsx(DropdownMenuPrimitive.Item, { "data-slot": "dropdown-menu-item", className: cn(itemClass, className), ...props }));
|
|
24
|
+
}
|
|
25
|
+
function DropdownMenuCheckboxItem({ className, children, ...props }) {
|
|
26
|
+
return (_jsxs(DropdownMenuPrimitive.CheckboxItem, { "data-slot": "dropdown-menu-checkbox-item", className: cn(itemClass, 'pl-7', className), ...props, children: [_jsx("span", { className: "absolute left-2 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Check, {}) }) }), children] }));
|
|
27
|
+
}
|
|
28
|
+
function DropdownMenuRadioGroup(props) {
|
|
29
|
+
return _jsx(DropdownMenuPrimitive.RadioGroup, { "data-slot": "dropdown-menu-radio-group", ...props });
|
|
30
|
+
}
|
|
31
|
+
function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
32
|
+
return (_jsxs(DropdownMenuPrimitive.RadioItem, { "data-slot": "dropdown-menu-radio-item", className: cn(itemClass, 'pl-7', className), ...props, children: [_jsx("span", { className: "absolute top-2 left-2 flex size-3.5 items-center justify-center", children: _jsx(DropdownMenuPrimitive.ItemIndicator, { children: _jsx(Check, {}) }) }), children] }));
|
|
33
|
+
}
|
|
34
|
+
function DropdownMenuSeparator({ className, ...props }) {
|
|
35
|
+
return (_jsx(DropdownMenuPrimitive.Separator, { "data-slot": "dropdown-menu-separator", className: cn('bg-border -mx-1 my-1 h-px', className), ...props }));
|
|
36
|
+
}
|
|
37
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuTrigger, };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
2
|
+
/** Every panel header shares this height so the columns read as one surface. */
|
|
3
|
+
declare function PanelHeader({ className, ...props }: ComponentProps<'div'>): import("react").JSX.Element;
|
|
4
|
+
declare function PanelTitle({ className, children, meta, ...props }: ComponentProps<'h2'> & {
|
|
5
|
+
meta?: ReactNode;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
declare function EmptyState({ icon, title, children, className, }: {
|
|
8
|
+
icon?: ReactNode;
|
|
9
|
+
title: string;
|
|
10
|
+
children?: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
declare function Kbd({ className, ...props }: ComponentProps<'kbd'>): import("react").JSX.Element;
|
|
14
|
+
export { EmptyState, Kbd, PanelHeader, PanelTitle };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { cn } from '../lib/cn.js';
|
|
3
|
+
/** Every panel header shares this height so the columns read as one surface. */
|
|
4
|
+
function PanelHeader({ className, ...props }) {
|
|
5
|
+
return (_jsx("div", { "data-slot": "panel-header", className: cn('border-border flex h-10 shrink-0 items-center gap-2 border-b px-3', className), ...props }));
|
|
6
|
+
}
|
|
7
|
+
function PanelTitle({ className, children, meta, ...props }) {
|
|
8
|
+
return (_jsxs("h2", { "data-slot": "panel-title", className: cn('flex min-w-0 items-baseline gap-2 text-[13px] font-medium', className), ...props, children: [_jsx("span", { className: "truncate", children: children }), meta !== undefined && meta !== null ? (_jsx("span", { className: "text-muted-foreground shrink-0 font-mono text-[11px] font-normal tabular-nums", children: meta })) : null] }));
|
|
9
|
+
}
|
|
10
|
+
function EmptyState({ icon, title, children, className, }) {
|
|
11
|
+
return (_jsxs("div", { className: cn('flex h-full min-h-32 flex-col items-center justify-center gap-2 px-6 text-center', className), children: [icon ? (_jsx("div", { className: "text-muted-foreground/70 border-border bg-card mb-1 flex size-8 items-center justify-center rounded-lg border [&_svg]:size-4", children: icon })) : null, _jsx("p", { className: "text-foreground text-[13px] font-medium", children: title }), children ? (_jsx("p", { className: "text-muted-foreground max-w-60 text-xs leading-relaxed", children: children })) : null] }));
|
|
12
|
+
}
|
|
13
|
+
function Kbd({ className, ...props }) {
|
|
14
|
+
return (_jsx("kbd", { "data-slot": "kbd", className: cn('border-border bg-background/60 text-muted-foreground pointer-events-none inline-flex h-5 min-w-5 items-center justify-center rounded border px-1 font-sans text-[10px] font-medium', className), ...props }));
|
|
15
|
+
}
|
|
16
|
+
export { EmptyState, Kbd, PanelHeader, PanelTitle };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Popover as PopoverPrimitive } from 'radix-ui';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
declare function Popover(props: React.ComponentProps<typeof PopoverPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function PopoverTrigger(props: React.ComponentProps<typeof PopoverPrimitive.Trigger>): React.JSX.Element;
|
|
5
|
+
declare function PopoverAnchor(props: React.ComponentProps<typeof PopoverPrimitive.Anchor>): React.JSX.Element;
|
|
6
|
+
declare function PopoverContent({ className, align, sideOffset, ...props }: React.ComponentProps<typeof PopoverPrimitive.Content>): React.JSX.Element;
|
|
7
|
+
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Popover as PopoverPrimitive } from 'radix-ui';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { cn } from '../lib/cn.js';
|
|
5
|
+
function Popover(props) {
|
|
6
|
+
return _jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
|
|
7
|
+
}
|
|
8
|
+
function PopoverTrigger(props) {
|
|
9
|
+
return _jsx(PopoverPrimitive.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
10
|
+
}
|
|
11
|
+
function PopoverAnchor(props) {
|
|
12
|
+
return _jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
13
|
+
}
|
|
14
|
+
function PopoverContent({ className, align = 'center', sideOffset = 6, ...props }) {
|
|
15
|
+
return (_jsx(PopoverPrimitive.Portal, { children: _jsx(PopoverPrimitive.Content, { "data-slot": "popover-content", align: align, sideOffset: sideOffset, className: cn('debugger-pop bg-card text-card-foreground border-border z-50 w-72 rounded-lg border p-3 shadow-xl shadow-black/30 outline-none', className), ...props }) }));
|
|
16
|
+
}
|
|
17
|
+
export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ComponentProps } from 'react';
|
|
2
|
+
import { Group, Panel, Separator } from 'react-resizable-panels';
|
|
3
|
+
declare function ResizablePanelGroup({ className, ...props }: ComponentProps<typeof Group>): import("react").JSX.Element;
|
|
4
|
+
declare function ResizablePanel(props: ComponentProps<typeof Panel>): import("react").JSX.Element;
|
|
5
|
+
/** A hairline divider with a wider invisible hit area that brightens while hovered or dragged. */
|
|
6
|
+
declare function ResizableHandle({ className, ...props }: ComponentProps<typeof Separator>): import("react").JSX.Element;
|
|
7
|
+
export { ResizableHandle, ResizablePanel, ResizablePanelGroup };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Group, Panel, Separator } from 'react-resizable-panels';
|
|
3
|
+
import { cn } from '../lib/cn.js';
|
|
4
|
+
function ResizablePanelGroup({ className, ...props }) {
|
|
5
|
+
return (_jsx(Group, { "data-slot": "resizable-panel-group", className: cn('flex size-full aria-[orientation=vertical]:flex-col', className), ...props }));
|
|
6
|
+
}
|
|
7
|
+
function ResizablePanel(props) {
|
|
8
|
+
return _jsx(Panel, { "data-slot": "resizable-panel", ...props });
|
|
9
|
+
}
|
|
10
|
+
/** A hairline divider with a wider invisible hit area that brightens while hovered or dragged. */
|
|
11
|
+
function ResizableHandle({ className, ...props }) {
|
|
12
|
+
return (_jsx(Separator, { "data-slot": "resizable-handle", className: cn('bg-border relative w-px shrink-0 outline-none after:absolute after:inset-y-0 after:left-1/2 after:w-2 after:-translate-x-1/2', 'data-[separator=active]:bg-ring data-[separator=focus]:bg-ring data-[separator=hover]:bg-ring/60 transition-colors duration-150', className), ...props }));
|
|
13
|
+
}
|
|
14
|
+
export { ResizableHandle, ResizablePanel, ResizablePanelGroup };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { ScrollArea as ScrollAreaPrimitive } from 'radix-ui';
|
|
2
2
|
import type { ComponentProps, Ref, UIEventHandler } from 'react';
|
|
3
|
-
declare function ScrollArea({ className, children, viewportRef, onViewportScroll, horizontal, ...props }: ComponentProps<typeof ScrollAreaPrimitive.Root> & {
|
|
3
|
+
declare function ScrollArea({ className, children, viewportRef, viewportClassName, onViewportScroll, horizontal, ...props }: ComponentProps<typeof ScrollAreaPrimitive.Root> & {
|
|
4
4
|
viewportRef?: Ref<HTMLDivElement>;
|
|
5
|
+
viewportClassName?: string;
|
|
5
6
|
onViewportScroll?: UIEventHandler<HTMLDivElement>;
|
|
6
7
|
horizontal?: boolean;
|
|
7
8
|
}): import("react").JSX.Element;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ScrollArea as ScrollAreaPrimitive } from 'radix-ui';
|
|
3
3
|
import { cn } from '../lib/cn.js';
|
|
4
|
-
function ScrollArea({ className, children, viewportRef, onViewportScroll, horizontal = false, ...props }) {
|
|
5
|
-
return (_jsxs(ScrollAreaPrimitive.Root, { "data-slot": "scroll-area", type: "
|
|
4
|
+
function ScrollArea({ className, children, viewportRef, viewportClassName, onViewportScroll, horizontal = false, ...props }) {
|
|
5
|
+
return (_jsxs(ScrollAreaPrimitive.Root, { "data-slot": "scroll-area", type: "hover", scrollHideDelay: 400, className: cn('relative overflow-hidden', className), ...props, children: [_jsx(ScrollAreaPrimitive.Viewport, { ref: viewportRef, "data-slot": "scroll-area-viewport", className: cn(
|
|
6
|
+
// Radix wraps content in `display: table`, which lets long lines widen the viewport.
|
|
7
|
+
'size-full overscroll-contain rounded-[inherit] outline-none [&>div]:block!', viewportClassName), onScroll: onViewportScroll, children: children }), _jsx(ScrollBar, {}), horizontal ? _jsx(ScrollBar, { orientation: "horizontal" }) : null, _jsx(ScrollAreaPrimitive.Corner, {})] }));
|
|
6
8
|
}
|
|
7
9
|
function ScrollBar({ className, orientation = 'vertical', ...props }) {
|
|
8
|
-
return (_jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, { "data-slot": "scroll-area-scrollbar", orientation: orientation, className: cn('flex touch-none p-px select-none', orientation === 'vertical' && 'h-full w-2 border-l border-l-transparent', orientation === 'horizontal' && 'h-2 flex-col border-t border-t-transparent', className), ...props, children: _jsx(ScrollAreaPrimitive.ScrollAreaThumb, { "data-slot": "scroll-area-thumb", className: "bg-
|
|
10
|
+
return (_jsx(ScrollAreaPrimitive.ScrollAreaScrollbar, { "data-slot": "scroll-area-scrollbar", orientation: orientation, className: cn('z-10 flex touch-none p-px transition-opacity duration-150 select-none data-[state=hidden]:opacity-0', orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent', orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent', className), ...props, children: _jsx(ScrollAreaPrimitive.ScrollAreaThumb, { "data-slot": "scroll-area-thumb", className: "bg-foreground/15 hover:bg-foreground/25 relative flex-1 rounded-full transition-colors" }) }));
|
|
9
11
|
}
|
|
10
12
|
export { ScrollArea, ScrollBar };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Select as SelectPrimitive } from 'radix-ui';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
declare function Select(props: React.ComponentProps<typeof SelectPrimitive.Root>): React.JSX.Element;
|
|
4
|
+
declare function SelectValue(props: React.ComponentProps<typeof SelectPrimitive.Value>): React.JSX.Element;
|
|
5
|
+
declare function SelectTrigger({ className, children, ...props }: React.ComponentProps<typeof SelectPrimitive.Trigger>): React.JSX.Element;
|
|
6
|
+
declare function SelectContent({ className, children, position, sideOffset, ...props }: React.ComponentProps<typeof SelectPrimitive.Content>): React.JSX.Element;
|
|
7
|
+
declare function SelectItem({ className, children, description, ...props }: React.ComponentProps<typeof SelectPrimitive.Item> & {
|
|
8
|
+
description?: string;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
export { Select, SelectContent, SelectItem, SelectTrigger, SelectValue };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Check, ChevronDown } from 'lucide-react';
|
|
3
|
+
import { Select as SelectPrimitive } from 'radix-ui';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import { cn } from '../lib/cn.js';
|
|
6
|
+
function Select(props) {
|
|
7
|
+
return _jsx(SelectPrimitive.Root, { "data-slot": "select", ...props });
|
|
8
|
+
}
|
|
9
|
+
function SelectValue(props) {
|
|
10
|
+
return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
|
|
11
|
+
}
|
|
12
|
+
function SelectTrigger({ className, children, ...props }) {
|
|
13
|
+
return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", className: cn('text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:ring-ring/50 data-[state=open]:bg-accent data-[state=open]:text-foreground flex h-7 items-center gap-1 rounded-md px-2 text-xs whitespace-nowrap transition-colors outline-none focus-visible:ring-[3px] disabled:opacity-50', className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDown, { className: "size-3 opacity-60" }) })] }));
|
|
14
|
+
}
|
|
15
|
+
function SelectContent({ className, children, position = 'popper', sideOffset = 6, ...props }) {
|
|
16
|
+
return (_jsx(SelectPrimitive.Portal, { children: _jsx(SelectPrimitive.Content, { "data-slot": "select-content", position: position, sideOffset: sideOffset, className: cn('debugger-pop bg-card text-card-foreground border-border z-50 min-w-40 overflow-hidden rounded-lg border p-1 shadow-xl shadow-black/30', className), ...props, children: _jsx(SelectPrimitive.Viewport, { children: children }) }) }));
|
|
17
|
+
}
|
|
18
|
+
function SelectItem({ className, children, description, ...props }) {
|
|
19
|
+
return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: cn('focus:bg-accent relative flex cursor-default flex-col items-start rounded-sm py-1.5 pr-2 pl-7 text-xs outline-none select-none data-disabled:opacity-50', className), ...props, children: [_jsx("span", { className: "absolute top-2 left-2 flex size-3.5 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(Check, { className: "size-3.5" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children }), description ? (_jsx("span", { className: "text-muted-foreground text-[11px]", children: description })) : null] }));
|
|
20
|
+
}
|
|
21
|
+
export { Select, SelectContent, SelectItem, SelectTrigger, SelectValue };
|
package/dist/debugger/ui/tabs.js
CHANGED
|
@@ -6,10 +6,10 @@ function Tabs({ className, ...props }) {
|
|
|
6
6
|
return (_jsx(TabsPrimitive.Root, { "data-slot": "tabs", className: cn('flex flex-col gap-2', className), ...props }));
|
|
7
7
|
}
|
|
8
8
|
function TabsList({ className, ...props }) {
|
|
9
|
-
return (_jsx(TabsPrimitive.List, { "data-slot": "tabs-list", className: cn('bg-secondary text-muted-foreground inline-flex h-
|
|
9
|
+
return (_jsx(TabsPrimitive.List, { "data-slot": "tabs-list", className: cn('bg-secondary/70 text-muted-foreground inline-flex h-7 w-fit shrink-0 items-center justify-center rounded-md p-0.5', className), ...props }));
|
|
10
10
|
}
|
|
11
11
|
function TabsTrigger({ className, ...props }) {
|
|
12
|
-
return (_jsx(TabsPrimitive.Trigger, { "data-slot": "tabs-trigger", className: cn('focus-visible:ring-ring/50 data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex h-
|
|
12
|
+
return (_jsx(TabsPrimitive.Trigger, { "data-slot": "tabs-trigger", className: cn('focus-visible:ring-ring/50 hover:text-foreground data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex h-6 items-center justify-center rounded-sm px-2 text-[11px] font-medium whitespace-nowrap transition-colors focus-visible:ring-[3px] focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm', className), ...props }));
|
|
13
13
|
}
|
|
14
14
|
function TabsContent({ className, ...props }) {
|
|
15
15
|
return (_jsx(TabsPrimitive.Content, { "data-slot": "tabs-content", className: cn('min-h-0 flex-1 outline-none', className), ...props }));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { cn } from '../lib/cn.js';
|
|
4
|
+
function Textarea({ className, ...props }) {
|
|
5
|
+
return (_jsx("textarea", { "data-slot": "textarea", className: cn('border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-2.5 py-2 text-[13px] leading-relaxed transition-[border-color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
|
|
6
|
+
}
|
|
7
|
+
export { Textarea };
|
package/dist/react/store.d.ts
CHANGED
|
@@ -45,7 +45,7 @@ export interface LoomsClientStore<TEvent extends AnyEventEnvelope = RegisteredEv
|
|
|
45
45
|
project<S>(definition: ProjectionDefinition<S>): S;
|
|
46
46
|
fold<S>(definition: EventFoldDefinition<S, TEvent>): S;
|
|
47
47
|
flush: () => void;
|
|
48
|
-
commit: (event: EventInput) => Promise<void>;
|
|
48
|
+
commit: (event: EventInput | readonly EventInput[]) => Promise<void>;
|
|
49
49
|
sync: () => Promise<void>;
|
|
50
50
|
dispose: () => void;
|
|
51
51
|
}
|
package/dist/react/store.js
CHANGED
|
@@ -302,7 +302,7 @@ export function createLoomsStore(options) {
|
|
|
302
302
|
try: (signal) => fetchFn(`${endpoint}/runs/${encodeURIComponent(options.storeId)}/events`, {
|
|
303
303
|
method: 'POST',
|
|
304
304
|
headers: { 'content-type': 'application/json' },
|
|
305
|
-
body: stringifyJson(event),
|
|
305
|
+
body: stringifyJson(Array.isArray(event) ? { events: event } : event),
|
|
306
306
|
signal,
|
|
307
307
|
}),
|
|
308
308
|
catch: (cause) => new LoomsStoreError('Run signal failed', cause),
|
package/dist/runtime/looms.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AnyRuntimeModule, type DefinitionInput, type DefinitionRef, type EventEnvelope, type EventInput, type EventStore, type EventStoreTrimCoverage, type JsonValue, type SnapshotStore, type ProjectionDefinition, type ReplayStep, type RunState } from '@swirls/looms/core';
|
|
1
|
+
import { type AnyRuntimeModule, type DebuggerHost, type DefinitionInput, type DefinitionRef, type EventEnvelope, type EventInput, type EventStore, type EventStoreTrimCoverage, type JsonValue, type SnapshotStore, type ProjectionDefinition, type ReplayStep, type RunState } from '@swirls/looms/core';
|
|
2
2
|
import type { RuntimeObserver } from './observer';
|
|
3
3
|
import { type EffectWorker, type LoomsRuntime, type RunOperationalStatus, type WakeScheduler, type WorkerCallbackInput } from './runtime';
|
|
4
4
|
import { type Authorize, type RunningServer } from './server';
|
|
@@ -33,8 +33,16 @@ export interface CreateLoomsOptions {
|
|
|
33
33
|
/**
|
|
34
34
|
* Authorizes HTTP API requests. Without it, run reads and writes are open while worker callback
|
|
35
35
|
* and operations routes return 503; `bearerAuth({ worker, operations })` enables them.
|
|
36
|
+
*
|
|
37
|
+
* A mounted debugger is authorized as `{ access: 'read', name: 'debugger' }`. Bearer tokens on
|
|
38
|
+
* reads block browser navigation, so hosts that need auth should use a cookie or session policy.
|
|
36
39
|
*/
|
|
37
40
|
readonly authorize?: Authorize;
|
|
41
|
+
/**
|
|
42
|
+
* Serves a debugger UI under `debugger.basePath` (for example `/debugger`). The runtime does not
|
|
43
|
+
* bundle a UI; pass `debuggerUi()` from `@swirls/looms/debugger/server`.
|
|
44
|
+
*/
|
|
45
|
+
readonly debugger?: DebuggerHost;
|
|
38
46
|
}
|
|
39
47
|
export interface StartResult {
|
|
40
48
|
readonly runId: string;
|
package/dist/runtime/looms.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Data, Effect, Predicate } from 'effect';
|
|
2
2
|
import { EventStoreTag, makeMemoryEventStore, snapshotStoreOf, SnapshotStoreTag, } from '@swirls/looms/core';
|
|
3
3
|
import { createRuntime, } from './runtime.js';
|
|
4
|
-
import { createFetchHandler,
|
|
4
|
+
import { createFetchHandler, handlesPath, mountDebugger, serveHttp, } from './server.js';
|
|
5
5
|
class LoomsInitializationError extends Data.TaggedError('LoomsInitializationError') {
|
|
6
6
|
constructor(cause) {
|
|
7
7
|
super({
|
|
@@ -13,6 +13,7 @@ class LoomsInitializationError extends Data.TaggedError('LoomsInitializationErro
|
|
|
13
13
|
}
|
|
14
14
|
export function createLooms(options = {}) {
|
|
15
15
|
const modules = options.modules ?? [];
|
|
16
|
+
const debuggerMount = options.debugger ? mountDebugger(options.debugger) : undefined;
|
|
16
17
|
let initPromise;
|
|
17
18
|
let runningServer;
|
|
18
19
|
const getInit = () => {
|
|
@@ -52,6 +53,7 @@ export function createLooms(options = {}) {
|
|
|
52
53
|
runtime,
|
|
53
54
|
store,
|
|
54
55
|
authorize: options.authorize,
|
|
56
|
+
debugger: debuggerMount,
|
|
55
57
|
});
|
|
56
58
|
return { runtime, store, snapshotStore: snapshotStoreOf(store), fetchHandler };
|
|
57
59
|
}));
|
|
@@ -98,7 +100,7 @@ export function createLooms(options = {}) {
|
|
|
98
100
|
cancel: (runId, threadId) => runEffect((i) => i.runtime.cancel(runId, threadId)),
|
|
99
101
|
workerCallback: (runId, input) => runEffect((i) => i.runtime.workerCallback(runId, input)),
|
|
100
102
|
fetch: (req) => {
|
|
101
|
-
if (!
|
|
103
|
+
if (!handlesPath(new URL(req.url).pathname, debuggerMount)) {
|
|
102
104
|
return Promise.resolve(null);
|
|
103
105
|
}
|
|
104
106
|
return getInit().then(({ fetchHandler }) => fetchHandler(req));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Clock, Data, Effect, Option } from 'effect';
|
|
2
|
-
import { advanceCursor, assignSequences, createCoalescingAppender, emptyCursor, EventStoreTrimmedError, hashRunState, trimEventStoreSafely, } from '@swirls/looms/core';
|
|
2
|
+
import { advanceCursor, assignSequences, createCoalescingAppender, emptyCursor, EventStoreTrimmedError, hashRunState, summarizeRun, trimEventStoreSafely, } from '@swirls/looms/core';
|
|
3
3
|
import { RuntimeExecutionError } from './errors.js';
|
|
4
4
|
import { stripSeq } from './helpers.js';
|
|
5
5
|
import { notifyObserver } from './observer.js';
|
|
@@ -36,7 +36,18 @@ function readRemaining(store, runId, events, expectedTail) {
|
|
|
36
36
|
}
|
|
37
37
|
export function createRuntimeRepository(options) {
|
|
38
38
|
const { registry, snapshotStore, trimAfterSnapshot, keepSnapshots, runCache, executionContexts } = options;
|
|
39
|
+
const headerFingerprints = new Map();
|
|
39
40
|
const observe = (event) => notifyObserver(options.observer, event);
|
|
41
|
+
const rememberHeader = (state) => {
|
|
42
|
+
const header = summarizeRun(state);
|
|
43
|
+
const fingerprint = `${header.status}\0${header.kind ?? ''}\0${header.definitionName ?? ''}`;
|
|
44
|
+
if (headerFingerprints.get(header.runId) === fingerprint) {
|
|
45
|
+
return Effect.void;
|
|
46
|
+
}
|
|
47
|
+
return snapshotStore.saveHeader(header).pipe(Effect.tap(() => Effect.sync(() => {
|
|
48
|
+
headerFingerprints.set(header.runId, fingerprint);
|
|
49
|
+
})), Effect.catch((error) => Effect.logWarning(`Run header persistence failed for "${header.runId}"`, error)));
|
|
50
|
+
};
|
|
40
51
|
const appendObserved = (store, runId, events, appendOptions) => Effect.suspend(() => {
|
|
41
52
|
const startedAt = currentTimeMillis();
|
|
42
53
|
return store.append(runId, events, appendOptions).pipe(Effect.tap(() => Effect.sync(() => observe({
|
|
@@ -156,6 +167,7 @@ export function createRuntimeRepository(options) {
|
|
|
156
167
|
return yield* loadCursorCold(store, runId);
|
|
157
168
|
}
|
|
158
169
|
if (tail === cached.seq) {
|
|
170
|
+
yield* rememberHeader(cached.state);
|
|
159
171
|
return cached;
|
|
160
172
|
}
|
|
161
173
|
const next = yield* catchUp(store, runId, cached, tail).pipe(Effect.catchTag('EventStoreTrimmedError', () => {
|
|
@@ -163,10 +175,12 @@ export function createRuntimeRepository(options) {
|
|
|
163
175
|
return loadCursorCold(store, runId);
|
|
164
176
|
}));
|
|
165
177
|
runCache.set(runId, next);
|
|
178
|
+
yield* rememberHeader(next.state);
|
|
166
179
|
return next;
|
|
167
180
|
}
|
|
168
181
|
const next = yield* loadCursorCold(store, runId);
|
|
169
182
|
runCache.set(runId, next);
|
|
183
|
+
yield* rememberHeader(next.state);
|
|
170
184
|
return next;
|
|
171
185
|
});
|
|
172
186
|
/**
|
|
@@ -241,6 +255,7 @@ export function createRuntimeRepository(options) {
|
|
|
241
255
|
next = yield* catchUp(store, runId, next, appendResult.tail);
|
|
242
256
|
}
|
|
243
257
|
runCache.set(runId, next);
|
|
258
|
+
yield* rememberHeader(next.state);
|
|
244
259
|
return next;
|
|
245
260
|
});
|
|
246
261
|
/**
|
package/dist/runtime/runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Clock, Effect, ManagedRuntime, Predicate } from 'effect';
|
|
2
|
-
import { composeModules, createEvent, createThreadId, createRunId, DEFAULT_DEFINITION_VERSION, DEFAULT_SNAPSHOT_EVERY, definitionKey, EventStoreTag, foldRun, project, replayTo, } from '@swirls/looms/core';
|
|
2
|
+
import { composeModules, createEvent, createThreadId, createRunId, DEFAULT_DEFINITION_VERSION, DEFAULT_SNAPSHOT_EVERY, definitionKey, EventStoreTag, foldRun, project, replayTo, summarizeRun, } from '@swirls/looms/core';
|
|
3
3
|
import { createCancellation } from './cancellation.js';
|
|
4
4
|
import { nextRuntimeDeadline } from './deadline-transition-planner.js';
|
|
5
5
|
import { RuntimeExecutionError } from './errors.js';
|
|
@@ -134,6 +134,21 @@ export function createRuntime(options) {
|
|
|
134
134
|
const store = yield* EventStoreTag;
|
|
135
135
|
return yield* store.listRuns;
|
|
136
136
|
}),
|
|
137
|
+
listRunSummaries: Effect.gen(function* () {
|
|
138
|
+
const store = yield* EventStoreTag;
|
|
139
|
+
const runIds = yield* store.listRuns;
|
|
140
|
+
const indexed = yield* snapshotStore.listHeaders;
|
|
141
|
+
const byId = new Map(indexed.map((header) => [header.runId, header]));
|
|
142
|
+
const missing = runIds.filter((runId) => !byId.has(runId));
|
|
143
|
+
const filled = yield* Effect.forEach(missing, (runId) => runtime.getRun(runId).pipe(Effect.map(summarizeRun)), { concurrency: 8 });
|
|
144
|
+
for (const header of filled) {
|
|
145
|
+
byId.set(header.runId, header);
|
|
146
|
+
}
|
|
147
|
+
return runIds.flatMap((runId) => {
|
|
148
|
+
const header = byId.get(runId);
|
|
149
|
+
return header ? [header] : [];
|
|
150
|
+
});
|
|
151
|
+
}),
|
|
137
152
|
getEvents: (runId, readOptions) => Effect.gen(function* () {
|
|
138
153
|
const store = yield* EventStoreTag;
|
|
139
154
|
return yield* store.read(runId, readOptions);
|
|
@@ -22,6 +22,7 @@ export type AuthDecision = {
|
|
|
22
22
|
export type Authorize = (request: Request, route: RouteInfo) => Effect.Effect<AuthDecision>;
|
|
23
23
|
export declare const authAllowed: AuthDecision;
|
|
24
24
|
export declare function authDenied(status: number, error: string): AuthDecision;
|
|
25
|
+
export declare function authResponse(decision: AuthDecision): Response | null;
|
|
25
26
|
/**
|
|
26
27
|
* Used when a host supplies no `authorize`: run reads and writes stay open, while worker callbacks
|
|
27
28
|
* and operational endpoints are unavailable until a host opts in.
|
|
@@ -3,6 +3,11 @@ export const authAllowed = { allowed: true };
|
|
|
3
3
|
export function authDenied(status, error) {
|
|
4
4
|
return { allowed: false, status, error };
|
|
5
5
|
}
|
|
6
|
+
export function authResponse(decision) {
|
|
7
|
+
return decision.allowed
|
|
8
|
+
? null
|
|
9
|
+
: Response.json({ error: decision.error }, { status: decision.status });
|
|
10
|
+
}
|
|
6
11
|
const unauthorized = authDenied(401, 'Unauthorized');
|
|
7
12
|
function notConfigured(access) {
|
|
8
13
|
return authDenied(503, access === 'worker'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import type { DebuggerHost } from '@swirls/looms/core';
|
|
3
|
+
import { type Authorize } from './auth';
|
|
4
|
+
export interface DebuggerMount {
|
|
5
|
+
readonly basePath: string;
|
|
6
|
+
readonly host: DebuggerHost;
|
|
7
|
+
}
|
|
8
|
+
export declare function mountDebugger(host: DebuggerHost): DebuggerMount;
|
|
9
|
+
/** Path below the mount, or null when `pathname` is outside it. */
|
|
10
|
+
export declare function debuggerPath(mount: DebuggerMount, pathname: string): string | null;
|
|
11
|
+
export declare function serveDebugger(req: Request, mount: DebuggerMount, path: string, authorize: Authorize): Effect.Effect<Response | null>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Effect } from 'effect';
|
|
2
|
+
import { authResponse } from './auth.js';
|
|
3
|
+
import { isLoomsApiPath } from './http.js';
|
|
4
|
+
export function mountDebugger(host) {
|
|
5
|
+
const path = host.basePath;
|
|
6
|
+
if (!path.startsWith('/') || path.includes('//') || path.split('/').includes('..')) {
|
|
7
|
+
throw new Error(`debugger basePath must be an absolute URL path: ${path}`);
|
|
8
|
+
}
|
|
9
|
+
const basePath = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
10
|
+
if (basePath === '') {
|
|
11
|
+
throw new Error('debugger basePath must not be /');
|
|
12
|
+
}
|
|
13
|
+
if (isLoomsApiPath(basePath)) {
|
|
14
|
+
throw new Error(`debugger basePath overlaps the HTTP API: ${basePath}`);
|
|
15
|
+
}
|
|
16
|
+
return { basePath, host };
|
|
17
|
+
}
|
|
18
|
+
/** Path below the mount, or null when `pathname` is outside it. */
|
|
19
|
+
export function debuggerPath(mount, pathname) {
|
|
20
|
+
if (pathname === mount.basePath) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
return pathname.startsWith(`${mount.basePath}/`) ? pathname.slice(mount.basePath.length) : null;
|
|
24
|
+
}
|
|
25
|
+
export function serveDebugger(req, mount, path, authorize) {
|
|
26
|
+
return authorize(req, { access: 'read', name: 'debugger' }).pipe(Effect.flatMap((decision) => {
|
|
27
|
+
const denied = authResponse(decision);
|
|
28
|
+
return denied
|
|
29
|
+
? Effect.succeed(denied)
|
|
30
|
+
: Effect.promise(() => mount.host.fetch(req, { basePath: mount.basePath, path }));
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { EventStore } from '@swirls/looms/core';
|
|
2
2
|
import type { LoomsRuntime } from '../runtime';
|
|
3
3
|
import { type Authorize } from './auth';
|
|
4
|
+
import { type DebuggerMount } from './debugger-host';
|
|
4
5
|
export interface FetchHandlerOptions {
|
|
5
6
|
readonly runtime: LoomsRuntime;
|
|
6
7
|
readonly store: EventStore;
|
|
@@ -9,6 +10,9 @@ export interface FetchHandlerOptions {
|
|
|
9
10
|
* operations routes unavailable; use `bearerAuth` or your own policy to change that.
|
|
10
11
|
*/
|
|
11
12
|
readonly authorize?: Authorize;
|
|
13
|
+
/** Debugger UI served beside the API and authorized as `{ access: 'read', name: 'debugger' }`. */
|
|
14
|
+
readonly debugger?: DebuggerMount;
|
|
12
15
|
}
|
|
13
|
-
|
|
16
|
+
/** Whether the fetch handler answers `pathname`: the HTTP API or the debugger mount. */
|
|
17
|
+
export declare function handlesPath(pathname: string, mount: DebuggerMount | undefined): boolean;
|
|
14
18
|
export declare function createFetchHandler(options: FetchHandlerOptions): (req: Request) => Promise<Response | null>;
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import { Effect, Predicate } from 'effect';
|
|
2
|
-
import { defaultAuthorize } from './auth.js';
|
|
3
|
-
import {
|
|
2
|
+
import { authResponse, defaultAuthorize } from './auth.js';
|
|
3
|
+
import { debuggerPath, serveDebugger } from './debugger-host.js';
|
|
4
|
+
import { acceptsEventStream, isLoomsApiPath, toErrorResponse } from './http.js';
|
|
4
5
|
import { matchRoute } from './route-table.js';
|
|
6
|
+
import { definitionRoutes } from './routes/definitions.js';
|
|
5
7
|
import { effectRoutes } from './routes/effects.js';
|
|
6
8
|
import { operationRoutes } from './routes/operations.js';
|
|
7
9
|
import { runRoutes } from './routes/runs.js';
|
|
8
10
|
function createRoutes(options) {
|
|
9
|
-
return [
|
|
11
|
+
return [
|
|
12
|
+
...definitionRoutes(options),
|
|
13
|
+
...runRoutes(options),
|
|
14
|
+
...effectRoutes(options),
|
|
15
|
+
...operationRoutes(options),
|
|
16
|
+
];
|
|
10
17
|
}
|
|
11
18
|
function authorizeRoute(req, matched, authorize) {
|
|
12
19
|
const { access, name } = matched.route;
|
|
@@ -14,21 +21,22 @@ function authorizeRoute(req, matched, authorize) {
|
|
|
14
21
|
return Effect.succeed(null);
|
|
15
22
|
}
|
|
16
23
|
const runId = matched.params.runId;
|
|
17
|
-
return authorize(req, Predicate.isString(runId) ? { access, name, runId } : { access, name }).pipe(Effect.map(
|
|
18
|
-
? null
|
|
19
|
-
: Response.json({ error: decision.error }, { status: decision.status })));
|
|
24
|
+
return authorize(req, Predicate.isString(runId) ? { access, name, runId } : { access, name }).pipe(Effect.map(authResponse));
|
|
20
25
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
path.startsWith('/runs/') ||
|
|
25
|
-
path.startsWith('/operations/'));
|
|
26
|
+
/** Whether the fetch handler answers `pathname`: the HTTP API or the debugger mount. */
|
|
27
|
+
export function handlesPath(pathname, mount) {
|
|
28
|
+
return isLoomsApiPath(pathname) || (mount !== undefined && debuggerPath(mount, pathname) !== null);
|
|
26
29
|
}
|
|
27
30
|
export function createFetchHandler(options) {
|
|
28
31
|
const routes = createRoutes(options);
|
|
29
32
|
const authorize = options.authorize ?? defaultAuthorize;
|
|
33
|
+
const mount = options.debugger;
|
|
30
34
|
return (req) => {
|
|
31
35
|
const url = new URL(req.url);
|
|
36
|
+
const mountedPath = mount ? debuggerPath(mount, url.pathname) : null;
|
|
37
|
+
if (mount && mountedPath !== null) {
|
|
38
|
+
return respond(serveDebugger(req, mount, mountedPath, authorize));
|
|
39
|
+
}
|
|
32
40
|
if (!isLoomsApiPath(url.pathname)) {
|
|
33
41
|
return Promise.resolve(null);
|
|
34
42
|
}
|
|
@@ -36,7 +44,7 @@ export function createFetchHandler(options) {
|
|
|
36
44
|
if (req.method === 'GET' && accept.includes('text/html') && !acceptsEventStream(req)) {
|
|
37
45
|
return Promise.resolve(null);
|
|
38
46
|
}
|
|
39
|
-
return
|
|
47
|
+
return respond(Effect.gen(function* () {
|
|
40
48
|
const matched = matchRoute(routes, req.method, url.pathname);
|
|
41
49
|
if (!matched) {
|
|
42
50
|
return new Response('Not Found', { status: 404 });
|
|
@@ -46,6 +54,9 @@ export function createFetchHandler(options) {
|
|
|
46
54
|
return denied;
|
|
47
55
|
}
|
|
48
56
|
return yield* matched.route.handle({ req, url, params: matched.params });
|
|
49
|
-
})
|
|
57
|
+
}));
|
|
50
58
|
};
|
|
51
59
|
}
|
|
60
|
+
function respond(effect) {
|
|
61
|
+
return Effect.runPromise(effect.pipe(Effect.catch((error) => Effect.succeed(toErrorResponse(error))), Effect.catchCause((cause) => Effect.logError('[@swirls/looms/runtime] request failed', cause).pipe(Effect.as(Response.json({ error: 'Internal server error' }, { status: 500 }))))));
|
|
62
|
+
}
|
|
@@ -26,6 +26,7 @@ export declare const EventInputBodySchema: Schema.Struct<{
|
|
|
26
26
|
readonly payload: Schema.Codec<Schema.Json, Schema.Json, never, never>;
|
|
27
27
|
readonly threadId: Schema.optional<Schema.NullOr<Schema.String>>;
|
|
28
28
|
}>;
|
|
29
|
+
export declare function isLoomsApiPath(path: string): boolean;
|
|
29
30
|
export declare function provideStore<A, E>(effect: Effect.Effect<A, E, EventStoreTag>, store: EventStore): Effect.Effect<A, E>;
|
|
30
31
|
export declare const readJson: (req: Request) => Effect.Effect<JsonValue, RequestBodyError>;
|
|
31
32
|
export declare function extractIdempotencyKey(req: Request, bodyKey?: string): string | undefined;
|
|
@@ -14,6 +14,10 @@ export const EventInputBodySchema = Schema.Struct({
|
|
|
14
14
|
payload: JsonValueSchema,
|
|
15
15
|
threadId: Schema.optional(Schema.NullOr(Schema.String)),
|
|
16
16
|
});
|
|
17
|
+
const LOOMS_API_ROOTS = ['/health', '/runs', '/definitions', '/operations'];
|
|
18
|
+
export function isLoomsApiPath(path) {
|
|
19
|
+
return LOOMS_API_ROOTS.some((root) => path === root || path.startsWith(`${root}/`));
|
|
20
|
+
}
|
|
17
21
|
export function provideStore(effect, store) {
|
|
18
22
|
return Effect.provideService(effect, EventStoreTag, store);
|
|
19
23
|
}
|