diff --git a/.gitignore b/.gitignore index 89ab3018..e726cc12 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ doc/feature +node_modules/ diff --git a/README.md b/README.md index 906c3999..2b542679 100644 --- a/README.md +++ b/README.md @@ -305,6 +305,143 @@ Paste text ending in `\n` is treated as line-wise. - While a mirror is in flight, `p` / `P` use the shadow so immediate yank/delete → put stays ordered. - Pi owns the terminal clipboard backends; on Wayland external state may lag while the shadow stays authoritative for immediate puts. +## compatible editor delegation and load order + +pi-vim intentionally uses Pi extension load order. Install pi-vim after another editor extension when Vim modal behavior should win. If that preceding editor is compatible, pi-vim preserves it as the INSERT-mode delegate and backing primitive editor. + +Delegation requires a Pi runtime that exposes `ctx.ui.getEditorComponent()`. Older runtimes without that API still get standalone pi-vim behavior, but pi-vim cannot preserve a previous editor there. + +pi-vim owns NORMAL mode, EX mode, escape handling, operators, motions, registers, and the mode label. The preceding editor receives ordinary INSERT-mode input only when it exposes the required `CustomEditor` / TUI `Editor`-compatible surface and internals that pi-vim needs for text, cursor, rendering, and primitive edits. + +If the preceding editor is incompatible or its factory fails, pi-vim replaces it with standalone pi-vim behavior and shows a warning for that mounted editor. [`@jordyvd/pi-image-attachments`](https://www.npmjs.com/package/@jordyvd/pi-image-attachments) is compatible when its editor is built on or preserves `CustomEditor` behavior. + +pi-vim remains structurally wrappable by later decorators that explicitly preserve pi-vim's surface. Prefer installing pi-vim last unless a later decorator documents pi-vim support. + +For maintainers and extension authors, pi-vim intentionally mixes passthrough, replacement, and chained delegation depending on the editor surface. The exact composition rules are below. + +### delegation model + +Glossary: + +- **outer editor** — the `ModalEditor` instance installed by pi-vim via `setEditorComponent`. +- **insert delegate** — a compatible previous editor stored as `insertDelegate`. +- **primitive editor** — the object used for low-level text/cursor operations; this is `insertDelegate` when present, otherwise pi-vim itself. +- **app action** — an entry in `actionHandlers`, such as `app.interrupt`, `app.exit`, or another keybinding-backed command. +- **extension shortcut** — `onExtensionShortcut(data): boolean`; `true` means "handled, stop here" and `false` means "let the next layer try". +- **delegate sync** — `syncInsertDelegate()`, which wires the outer editor's runtime surface onto the insert delegate before delegated input or rendering. + +pi-vim uses three delegation patterns: + +| pattern | where it applies | behavior | +|---------|------------------|----------| +| delegate / passthrough | Ordinary INSERT input and primitive text edits | pi-vim calls the insert delegate / backing primitive editor | +| replace / block | NORMAL mode, EX mode, mode labels, same-key `actionHandlers` | pi-vim owns the behavior; the delegate does not also handle it | +| run and delegate | Callback fields such as `onSubmit`, `onChange`, `onEscape`, `onCtrlD`, `onPasteImage`; `onExtensionShortcut` when unhandled | pi-vim preserves the outer callback and the delegate callback where the API supports chaining | + +Input flow: + +```text +handleInput(data) + ├─ EX mode: pi-vim handles the mini-command line + ├─ NORMAL mode: pi-vim handles modal commands, operators, and motions + └─ INSERT mode: + ├─ paste / key-release guards run in pi-vim + └─ ordinary editor input goes to insertDelegate.handleInput(data) +``` + +pi-vim is therefore not a transparent wrapper. It is a modal router with an INSERT-mode backing editor. + +#### callback fields + +Single callback fields are chained when both pi-vim and the delegate may need to observe the event. For `onSubmit` and `onChange`, delegate sync installs a wrapper equivalent to: + +```text +outer callback +then delegate callback +``` + +`onEscape`, `onCtrlD`, and `onPasteImage` follow the same outer-then-delegate chaining shape. + +`onExtensionShortcut` is different because it has a boolean "handled" contract: + +```text +outer onExtensionShortcut(data) + ├─ returns true -> stop; delegate is blocked + └─ returns false -> delegate may try the shortcut +``` + +For example: + +```ts +editor.onExtensionShortcut = (data) => { + if (data === "\x1bt") { + toggleTodoPanel(); + return true; + } + + return false; +}; +``` + +Returning `true` prevents the same key from also becoming text input or triggering another shortcut layer. + +#### actionHandlers + +`actionHandlers` are not chained. They are a map from one app action to one function: + +```ts +Map void> +``` + +For same-key actions, pi-vim uses **outer-wins** replacement. Chaining same-key app actions would double-run commands such as toggles, exits, or interrupts. + +`syncActionHandlers()` reconciles the delegate map with the outer map: + +1. Remove stale handlers that pi-vim previously copied, but only if the delegate still points at the exact copied function. +2. Copy current outer handlers into the delegate. +3. Preserve delegate-only handlers. +4. Preserve delegate replacements only after the outer editor stops owning that action. + +If the outer editor still owns the same action key, the next sync overwrites the delegate's replacement again. Outer wins while it owns the action. + +Why copy handlers into the delegate at all? + +In INSERT mode, pi-vim delegates input to `insertDelegate.handleInput(data)`. At that point, the delegate's `CustomEditor.handleInput()` is the code checking app actions. If the runtime installed app handlers on the outer pi-vim editor, the delegate must see those handlers too, or delegated INSERT input would stop honoring app shortcuts. + +Example: + +```text +top-level editor = pi-vim +insert delegate = image attachments editor + +runtime installs app.openCommandPalette on pi-vim + +user presses ctrl-p in INSERT mode + -> pi-vim delegates to imageEditor.handleInput(ctrl-p) + -> imageEditor checks imageEditor.actionHandlers +``` + +Without `syncActionHandlers()`, `imageEditor.actionHandlers` would not contain `app.openCommandPalette`, so the shortcut would be lost while INSERT input is delegated. + +The identity check in `syncActionHandlers()` prevents destructive cleanup: + +```text +pi-vim copied action B into delegate +delegate later replaces B with its own handler +outer pi-vim removes B +syncActionHandlers sees delegate B is no longer the copied function +so it leaves delegate B alone +``` + +So the rule is: + +```text +same action key: outer wins while outer owns it +delegate-only action: preserved +copied outer action removed later: cleaned up +delegate replacement after outer removal: preserved +``` + --- ## known differences from full Vim @@ -348,10 +485,11 @@ Explicitly deferred: ## architecture notes -- `index.ts` — `ModalEditor` subclass of `CustomEditor`; all key handling. +- `index.ts` — installed `ModalEditor`; all key handling, with an optional compatible preceding editor as the INSERT-mode delegate/backing primitive editor. - `motions.ts` — pure motion calculation helpers (`findWordMotionTarget`, `findCharMotionTarget`); no side effects. - `types.ts` — shared types and escape-sequence constants. +- `script/image-attachments-e2e.ts` — load-order E2E check for pi-vim with `@jordyvd/pi-image-attachments`. - `test/` — Node test runner suite; no browser / full runtime required. Run checks: diff --git a/index.ts b/index.ts index 7e9906b0..6a20e1f7 100644 --- a/index.ts +++ b/index.ts @@ -5,8 +5,11 @@ import { type ExtensionAPI, } from "@mariozechner/pi-coding-agent"; import { + type AutocompleteProvider, + type EditorComponent, CURSOR_MARKER, Key, + isKeyRelease, matchesKey, truncateToWidth, visibleWidth, @@ -78,6 +81,30 @@ const CLIPBOARD_SPAWN_FAILURE_LIMIT = 3; const CLIPBOARD_READ_TIMEOUT_MS = 750; const CLIPBOARD_READ_MAX_BUFFER_BYTES = 1024 * 1024; +function isKeyReleaseEvent(data: string): boolean { + return data.startsWith("\x1b[") && isKeyRelease(data); +} + +function escapeTerminalControls(data: string): string { + let escaped = ""; + for (const char of data) { + const codePoint = char.codePointAt(0); + if (codePoint === undefined) continue; + if (codePoint === 0x1b) { + escaped += "^["; + } else if (codePoint < 0x20) { + escaped += `^${String.fromCharCode(codePoint + 0x40)}`; + } else if (codePoint === 0x7f) { + escaped += "^?"; + } else if (codePoint >= 0x80 && codePoint <= 0x9f) { + escaped += `\\x${codePoint.toString(16).padStart(2, "0")}`; + } else { + escaped += char; + } + } + return escaped; +} + type EditorSnapshot = { text: string; cursor: { line: number; col: number }; @@ -96,16 +123,202 @@ type ModalEditorInternals = { setCursorCol?: (col: number) => void; }; -type CustomEditorConstructorArgs = ConstructorParameters; +export type CustomEditorCompatible = EditorComponent & { + getLines(): string[]; + getCursor(): { line: number; col: number }; + insertTextAtCursor(text: string): void; + state: { lines: string[]; cursorLine: number; cursorCol: number }; + pushUndoSnapshot(): void; +}; + +type CustomEditorHandlerSurface = Partial< + Pick< + CustomEditor, + "onSubmit" | "onChange" | "onEscape" | "onCtrlD" | "onPasteImage" | "onExtensionShortcut" | "actionHandlers" + > +>; + +type ActionHandlerMap = NonNullable; +type ActionHandlerKey = Parameters[0]; +type ActionHandler = Parameters[1]; +type ExSegment = { text: string; pasted: boolean }; +type DelegateHandlerFields = Pick< + CustomEditorHandlerSurface, + "onSubmit" | "onChange" | "onEscape" | "onCtrlD" | "onPasteImage" | "onExtensionShortcut" +>; +type OwnedDelegateHandlers = DelegateHandlerFields & { + delegate?: DelegateHandlerFields; + actionHandlers?: Map; +}; +type TextDelegateHandlerKey = "onSubmit" | "onChange"; +type VoidDelegateHandlerKey = "onEscape" | "onCtrlD" | "onPasteImage"; + +function chainVoid( + outerHandler: (() => void) | undefined, + delegateHandler: (() => void) | undefined, +): (() => void) | undefined { + if (!outerHandler || delegateHandler === outerHandler) return delegateHandler; + if (!delegateHandler) return outerHandler; + + return () => { + outerHandler(); + delegateHandler(); + }; +} + +function chainText( + outerHandler: ((text: string) => void) | undefined, + delegateHandler: ((text: string) => void) | undefined, +): ((text: string) => void) | undefined { + if (!outerHandler || delegateHandler === outerHandler) return delegateHandler; + if (!delegateHandler) return outerHandler; + + return (text: string) => { + outerHandler(text); + delegateHandler(text); + }; +} + +function chainExt( + outerHandler: ((data: string) => boolean) | undefined, + delegateHandler: ((data: string) => boolean) | undefined, +): ((data: string) => boolean) | undefined { + if (!outerHandler || delegateHandler === outerHandler) return delegateHandler; + if (!delegateHandler) return outerHandler; + + return (data: string) => outerHandler(data) || delegateHandler(data); +} + +// Track the wrappers pi-vim owns so repeated delegate syncs can recover the +// delegate's current handler instead of wrapping an old wrapper again. +function syncText( + handlerSurface: CustomEditorHandlerSurface, + ownedHandlers: OwnedDelegateHandlers, + handlerKey: TextDelegateHandlerKey, + outerHandler: ((text: string) => void) | undefined, +): void { + const delegateHandlers = ownedHandlers.delegate ?? {}; + const delegateHandler = handlerSurface[handlerKey] === ownedHandlers[handlerKey] + ? delegateHandlers[handlerKey] + : handlerSurface[handlerKey]; + + handlerSurface[handlerKey] = chainText(outerHandler, delegateHandler); + ownedHandlers[handlerKey] = handlerSurface[handlerKey]; + delegateHandlers[handlerKey] = delegateHandler; + ownedHandlers.delegate = delegateHandlers; +} + +function syncVoid( + handlerSurface: CustomEditorHandlerSurface, + ownedHandlers: OwnedDelegateHandlers, + handlerKey: VoidDelegateHandlerKey, + outerHandler: (() => void) | undefined, +): void { + const delegateHandlers = ownedHandlers.delegate ?? {}; + const delegateHandler = handlerSurface[handlerKey] === ownedHandlers[handlerKey] + ? delegateHandlers[handlerKey] + : handlerSurface[handlerKey]; + + handlerSurface[handlerKey] = chainVoid(outerHandler, delegateHandler); + ownedHandlers[handlerKey] = handlerSurface[handlerKey]; + delegateHandlers[handlerKey] = delegateHandler; + ownedHandlers.delegate = delegateHandlers; +} + +function syncExt( + handlerSurface: CustomEditorHandlerSurface, + ownedHandlers: OwnedDelegateHandlers, + outerHandler: ((data: string) => boolean) | undefined, +): void { + const delegateHandlers = ownedHandlers.delegate ?? {}; + const delegateHandler = handlerSurface.onExtensionShortcut === ownedHandlers.onExtensionShortcut + ? delegateHandlers.onExtensionShortcut + : handlerSurface.onExtensionShortcut; + + handlerSurface.onExtensionShortcut = chainExt(outerHandler, delegateHandler); + ownedHandlers.onExtensionShortcut = handlerSurface.onExtensionShortcut; + delegateHandlers.onExtensionShortcut = delegateHandler; + ownedHandlers.delegate = delegateHandlers; +} + +function syncActionHandlers( + delegateHandlers: ActionHandlerMap, + ownedHandlers: OwnedDelegateHandlers, + sourceHandlers: ActionHandlerMap, +): void { + const ownedActions = ownedHandlers.actionHandlers ?? new Map(); + + for (const [action, handler] of ownedActions) { + if (!sourceHandlers.has(action) && delegateHandlers.get(action) === handler) { + delegateHandlers.delete(action); + ownedActions.delete(action); + } + } + + for (const [action, handler] of sourceHandlers) { + delegateHandlers.set(action, handler); + ownedActions.set(action, handler); + } + + ownedHandlers.actionHandlers = ownedActions; +} + +export type CompatibilityResult = { compatible: true; editor: CustomEditorCompatible } | { compatible: false; reason: string }; + +export const REQUIRED_COMPATIBLE_METHODS = ["render", "invalidate", "handleInput", "getText", "setText", "getLines", "getCursor", "insertTextAtCursor", "pushUndoSnapshot"] as const; + +export function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} + +export function getCompatibility(editor: unknown): CompatibilityResult { + const fail = (reason: string) => ({ compatible: false, reason }) as const; + if (!isRecord(editor)) return fail("not object"); + const missing = REQUIRED_COMPATIBLE_METHODS.find((method) => typeof editor[method] !== "function"); + if (missing) return fail(`no ${missing}`); + const state = editor.state; + if (!isRecord(state)) return fail("no state"); + const { lines, cursorLine: line, cursorCol: col } = state; + if (!Array.isArray(lines)) return fail("no state.lines"); + if (!lines.length) return fail("empty state.lines"); + const badLine = lines.findIndex((line) => typeof line !== "string"); + if (badLine >= 0) return fail(`bad state.lines[${badLine}]`); + if (typeof line !== "number") return fail("no state.cursorLine"); + if (!Number.isInteger(line)) return fail("bad state.cursorLine"); + if (typeof col !== "number") return fail("no state.cursorCol"); + if (!Number.isInteger(col)) return fail("bad state.cursorCol"); + if (line < 0 || line >= lines.length) return fail(`bad state.cursorLine:${line}/${lines.length}`); + if (col < 0 || col > lines[line].length) return fail(`bad state.cursorCol:${col}/${lines[line].length}`); + return { compatible: true, editor: editor as unknown as CustomEditorCompatible }; +} + +export function formatUnknownError(error: unknown): string { + if (error instanceof Error) { + return error.message ? `${error.name}: ${error.message}` : error.name; + } + if (typeof error === "string") return error; + try { + return JSON.stringify(error) ?? String(error); + } catch { + return String(error); + } +} + type ClipboardWriteFn = (text: string, signal: AbortSignal) => Promise; type ClipboardReadFn = () => string | null; type ClipboardProcess = ReturnType; -type ModeLabelColorizers = { - insert: (s: string) => string; - normal: (s: string) => string; - ex: (s: string) => string; -}; +type ModeLabelColorizer = (text: string) => string; +type ModeLabelColorizers = Record<"insert" | "normal" | "ex", ModeLabelColorizer>; + +function isLegacyModeLabelColorizers(value: unknown): value is ModeLabelColorizers | null { + if (value === null) return true; + if (!isRecord(value)) return false; + + return typeof value.insert === "function" + && typeof value.normal === "function" + && typeof value.ex === "function"; +} type CursorShapeSequence = | typeof INSERT_CURSOR_SHAPE @@ -258,8 +471,6 @@ for await (const chunk of process.stdin) { try { await Promise.resolve(copyToClipboard(Buffer.concat(chunks).toString("utf8"))); } catch { - // Pi clipboard writes are best-effort. Backend failures must not make the - // helper exit non-zero and trip the parent spawn/environment breaker. } `; @@ -316,7 +527,7 @@ function killClipboardProcess(child: ClipboardProcess): void { try { child.kill("SIGKILL"); } catch { - // Best effort only; clipboard mirroring must not affect editing. + return; } } @@ -476,7 +687,6 @@ class ClipboardMirror { this.circuitBreaker.consecutiveEnvironmentFailures = 0; } catch (error) { this.recordWriteFailure(error); - // Clipboard mirroring is best-effort; the register is authoritative. } finally { if (this.activeController === controller) { this.activeController = null; @@ -528,77 +738,149 @@ class ClipboardMirror { } } -export class ModalEditor extends CustomEditor { +// biome-ignore lint/suspicious/noExplicitAny: mixin. +type CustomEditorConstructor = new (...args: any[]) => CustomEditor; + +export function createModalEditor(Base: TBase) { + return class ModalEditor extends Base { + private decoratorWantsKeyRelease?: boolean; + + get wantsKeyRelease(): boolean | undefined { + const delegateWantsKeyRelease = this.mode === "insert" + ? this.insertDelegate?.wantsKeyRelease + : undefined; + + if (this.decoratorWantsKeyRelease === true || delegateWantsKeyRelease === true) return true; + if (this.decoratorWantsKeyRelease === false) return false; + return delegateWantsKeyRelease === false ? false : undefined; + } + + set wantsKeyRelease(value: boolean | undefined) { + this.decoratorWantsKeyRelease = value; + } + private mode: Mode = "insert"; private pendingMotion: PendingMotion = null; private pendingTextObject: TextObjectKind | null = null; private pendingOperator: PendingOperator = null; - private prefixCount: string = ""; - private operatorCount: string = ""; - private pendingG: boolean = false; - private pendingGCount: string = ""; - private pendingReplace: boolean = false; + private prefixCount = ""; + private operatorCount = ""; + private pendingG = false; + private pendingGCount = ""; + private pendingReplace = false; private pendingExCommand: string | null = null; - private acceptingBracketedPasteInExCommand: boolean = false; - private pendingEscWhileAcceptingBracketedPasteInExCommand: boolean = false; + private acceptingBracketedPasteInExCommand = false; + private pendingEscWhileAcceptingBracketedPasteInExCommand = false; private lastCharMotion: LastCharMotion | null = null; - private discardingBracketedPasteInNormalMode: boolean = false; - private pendingEscWhileDiscardingBracketedPasteInNormalMode: boolean = false; + private discardingBracketedPasteInNormalMode = false; + private pendingEscWhileDiscardingBracketedPasteInNormalMode = false; + private forwardingBracketedPasteInInsertMode = false; private wordBoundaryCache = new WordBoundaryCache(); private readonly redoStack: EditorSnapshot[] = []; private currentTransition: TransitionState = "none"; - private onChangeHooked: boolean = false; - private readonly labelColorizers: ModeLabelColorizers | null; + private onChangeHooked = false; + private labelColorizers: ModeLabelColorizers | null = null; private readonly cursorShapeRuntime: CursorShapeRuntime | null; private lastCursorShapeSequence: CursorShapeSequence | null = null; + private insertDelegate: CustomEditorCompatible | null = null; + private readonly ownedDelegateHandlers = new WeakMap(); - // Unnamed register - private unnamedRegister: string = ""; - private clipboardMirrorPolicy: ClipboardMirrorPolicy = DEFAULT_CLIPBOARD_MIRROR_POLICY; + private unnamedRegister = ""; + private clipboardMirrorPolicy = DEFAULT_CLIPBOARD_MIRROR_POLICY; private readonly clipboardMirror = new ClipboardMirror(writeClipboardInChildProcess); - private clipboardReadFn: ClipboardReadFn = readClipboardInChildProcess; - private quitFn: () => void = () => {}; + private clipboardReadFn = readClipboardInChildProcess; + private quitFn = () => {}; private notifyFn: (message: string) => void = () => {}; - constructor( - tui: CustomEditorConstructorArgs[0], - theme: CustomEditorConstructorArgs[1], - kb: CustomEditorConstructorArgs[2], - labelColorizers?: ModeLabelColorizers | null, - ) { - super(tui, theme, kb); - this.cursorShapeRuntime = getCursorShapeRuntime(tui); - this.labelColorizers = labelColorizers ?? null; - } - - // Test seams - setClipboardFn(fn: (text: string, signal?: AbortSignal) => unknown): void { + // biome-ignore lint/suspicious/noExplicitAny: mixin. + constructor(...args: any[]) { + // Legacy pi-vim accepted mode-label colorizers as the fourth constructor + // arg. Only strip that exact shape; CustomEditor options must reach Base. + const fourthArg = args[3]; + const legacyColorizers = isLegacyModeLabelColorizers(fourthArg) + ? (args.splice(3, 1)[0] as ModeLabelColorizers | null) + : undefined; + + super(...args); + this.cursorShapeRuntime = getCursorShapeRuntime(args[0]); + + if (legacyColorizers !== undefined) { + this.labelColorizers = legacyColorizers; + } + } + + setColorizers(colorizers: ModeLabelColorizers | null) { + this.labelColorizers = colorizers ?? null; + } + + setClipboardFn(fn: (text: string, signal?: AbortSignal) => unknown) { this.clipboardMirror.setWriteFn(async (text: string, signal: AbortSignal) => { await fn(text, signal); }); } - setClipboardWriteTimeoutMs(timeoutMs: number): void { + setClipboardWriteTimeoutMs(timeoutMs: number) { this.clipboardMirror.setTimeoutMs(timeoutMs); } - setClipboardReadFn(fn: ClipboardReadFn): void { + setClipboardReadFn(fn: ClipboardReadFn) { this.clipboardReadFn = fn; } - setClipboardMirrorPolicy(policy: ClipboardMirrorPolicy): void { + setClipboardMirrorPolicy(policy: ClipboardMirrorPolicy) { this.clipboardMirrorPolicy = policy; } getClipboardMirrorPolicy(): ClipboardMirrorPolicy { return this.clipboardMirrorPolicy; } - setQuitFn(fn: () => void): void { this.quitFn = fn; } - setNotifyFn(fn: (message: string) => void): void { this.notifyFn = fn; } + setQuitFn(fn: () => void) { this.quitFn = fn; } + setNotifyFn(fn: (message: string) => void) { this.notifyFn = fn; } + setInsertDelegate(editor: CustomEditorCompatible) { this.insertDelegate = editor; } getRegister(): string { return this.unnamedRegister; } setRegister(text: string): void { this.unnamedRegister = text; } getMode(): Mode { return this.mode; } - getText(): string { return this.getLines().join("\n"); } + getText(): string { return this.insertDelegate?.getText() ?? this.getLines().join("\n"); } + getLines(): string[] { return this.insertDelegate?.getLines() ?? super.getLines(); } + getCursor(): { line: number; col: number } { return this.insertDelegate?.getCursor() ?? super.getCursor(); } + + private syncedDelegate(): CustomEditorCompatible | null { this.syncInsertDelegate(); return this.insertDelegate; } override setText(text: string): void { this.clearRedoStack(); - super.setText(text); + const editor = this.syncedDelegate(); + if (editor) editor.setText(text); else super.setText(text); + } + + override insertTextAtCursor(text: string): void { + const editor = this.syncedDelegate(); + if (editor) editor.insertTextAtCursor(text); else super.insertTextAtCursor(text); + } + + override getExpandedText(): string { + const editor = this.insertDelegate; + return editor ? editor.getExpandedText?.() ?? editor.getText() : super.getExpandedText(); + } + + override addToHistory(text: string): void { + const editor = this.syncedDelegate(); + if (editor) editor.addToHistory?.(text); else super.addToHistory(text); + } + + override setAutocompleteProvider(provider: AutocompleteProvider): void { + const editor = this.syncedDelegate(); + if (editor) editor.setAutocompleteProvider?.(provider); else super.setAutocompleteProvider(provider); + } + + override setPaddingX(padding: number): void { + const editor = this.syncedDelegate(); + if (editor) editor.setPaddingX?.(padding); else super.setPaddingX(padding); + } + + override setAutocompleteMaxVisible(maxVisible: number): void { + const editor = this.syncedDelegate(); + if (editor) editor.setAutocompleteMaxVisible?.(maxVisible); else super.setAutocompleteMaxVisible(maxVisible); + } + + override invalidate(): void { + const editor = this.syncedDelegate(); + if (editor) editor.invalidate(); else super.invalidate(); } private captureSnapshot(): EditorSnapshot { @@ -619,8 +901,39 @@ export class ModalEditor extends CustomEditor { return state as { lines: string[]; cursorLine?: number; cursorCol?: number }; } + private primitiveEditor(): ModalEditorInternals { + return (this.insertDelegate ?? this) as unknown as ModalEditorInternals; + } + + private syncDelegateTextAfterDirectMutation( + editor: ModalEditorInternals, + text: string, + cursor: { line: number; col: number }, + ): boolean { + const delegate = this.insertDelegate; + if (!delegate || (editor as unknown) !== delegate) return false; + + this.syncInsertDelegate(); + delegate.setText(text); + + const state = editor.state; + if (!state || !Array.isArray(state.lines)) return true; + + editor.historyIndex = -1; + editor.lastAction = null; + state.cursorLine = cursor.line; + if (typeof editor.setCursorCol === "function") { + editor.setCursorCol(cursor.col); + } else { + state.cursorCol = cursor.col; + editor.preferredVisualCol = null; + } + + return true; + } + private restoreSnapshot(snapshot: EditorSnapshot): void { - const editor = this as unknown as ModalEditorInternals; + const editor = this.primitiveEditor(); const state = this.requireRedoRestoreState(editor); const lines = snapshot.text.split("\n"); @@ -643,7 +956,12 @@ export class ModalEditor extends CustomEditor { editor.historyIndex = -1; editor.lastAction = null; - editor.onChange?.(this.getText()); + const synced = this.syncDelegateTextAfterDirectMutation( + editor, + snapshot.text, + { line: cursorLine, col: cursorCol }, + ); + if (!synced) editor.onChange?.(this.getText()); editor.tui?.requestRender?.(); } @@ -672,7 +990,7 @@ export class ModalEditor extends CustomEditor { let changed = false; this.withTransition("undo", () => { const beforeUndo = this.captureSnapshot(); - super.handleInput(CTRL_UNDERSCORE); + this.applyEditorPrimitive(CTRL_UNDERSCORE); const afterUndo = this.captureSnapshot(); if (this.snapshotChanged(beforeUndo, afterUndo)) { @@ -686,7 +1004,7 @@ export class ModalEditor extends CustomEditor { private performRedo(count: number = this.takeTotalCount(1)): void { const maxSteps = Math.max(1, Math.min(MAX_COUNT, count)); - const editor = this as unknown as ModalEditorInternals; + const editor = this.primitiveEditor(); for (let i = 0; i < maxSteps; i++) { const snapshot = this.redoStack[this.redoStack.length - 1]; @@ -726,6 +1044,7 @@ export class ModalEditor extends CustomEditor { }; this.onChangeHooked = true; + this.syncInsertDelegate(); } private centralInvalidationCheck(): void { @@ -735,7 +1054,7 @@ export class ModalEditor extends CustomEditor { } private applySyntheticEdit(mutation: () => void): void { - const editor = this as unknown as ModalEditorInternals; + const editor = this.primitiveEditor(); if (!editor.state || !Array.isArray(editor.state.lines)) { throw new Error( "Synthetic edit prerequisite: editor state unavailable", @@ -756,9 +1075,6 @@ export class ModalEditor extends CustomEditor { if (this.getText() === textBefore) return; - // Text changed — push undo boundary for pre-mutation state. - // Briefly swap pre-mutation state in for the snapshot, then - // restore the post-mutation result. const postLines = editor.state.lines.slice(); const postCursorLine = editor.state.cursorLine; const postCursorCol = editor.state.cursorCol; @@ -775,7 +1091,13 @@ export class ModalEditor extends CustomEditor { editor.state.cursorCol = postCursorCol; editor.preferredVisualCol = postPreferredCol; - editor.onChange?.(this.getText()); + const postText = this.getText(); + const synced = this.syncDelegateTextAfterDirectMutation( + editor, + postText, + { line: postCursorLine ?? 0, col: postCursorCol ?? 0 }, + ); + if (!synced) editor.onChange?.(postText); editor.tui?.requestRender?.(); } @@ -815,9 +1137,19 @@ export class ModalEditor extends CustomEditor { return matchesKey(data, "escape") || matchesKey(data, "ctrl+["); } - private normalizePendingExCommandInput(data: string): string | null { + private normalizePendingExCommandInput(data: string): ExSegment[] | null { let chunk = data; - let normalized = ""; + const segments: ExSegment[] = []; + const appendSegment = (text: string, pasted: boolean) => { + if (!text) return; + const previous = segments.at(-1); + if (previous?.pasted === pasted) { + previous.text += text; + return; + } + segments.push({ text, pasted }); + }; + const normalizedOrNull = () => segments.length > 0 ? segments : null; while (true) { if (this.acceptingBracketedPasteInExCommand) { @@ -827,46 +1159,46 @@ export class ModalEditor extends CustomEditor { this.acceptingBracketedPasteInExCommand = false; chunk = chunk.slice(BRACKETED_PASTE_END_TAIL.length); if (chunk.length === 0) { - return normalized.length > 0 ? normalized : null; + return normalizedOrNull(); } continue; } - normalized += "\x1b"; + appendSegment("\x1b", true); this.pendingEscWhileAcceptingBracketedPasteInExCommand = false; } const end = chunk.indexOf(BRACKETED_PASTE_END); if (end !== -1) { - normalized += chunk.slice(0, end); + appendSegment(chunk.slice(0, end), true); this.acceptingBracketedPasteInExCommand = false; chunk = chunk.slice(end + BRACKETED_PASTE_END.length); if (chunk.length === 0) { - return normalized.length > 0 ? normalized : null; + return normalizedOrNull(); } continue; } if (this.isEscapeLikeInput(chunk)) { this.pendingEscWhileAcceptingBracketedPasteInExCommand = true; - return normalized.length > 0 ? normalized : null; + return normalizedOrNull(); } - normalized += chunk; - return normalized.length > 0 ? normalized : null; + appendSegment(chunk, true); + return normalizedOrNull(); } const start = chunk.indexOf(BRACKETED_PASTE_START); if (start === -1) { - normalized += chunk; - return normalized.length > 0 ? normalized : null; + appendSegment(chunk, false); + return normalizedOrNull(); } - normalized += chunk.slice(0, start); + appendSegment(chunk.slice(0, start), false); chunk = chunk.slice(start + BRACKETED_PASTE_START.length); this.acceptingBracketedPasteInExCommand = true; if (chunk.length === 0) { - return normalized.length > 0 ? normalized : null; + return normalizedOrNull(); } } } @@ -906,13 +1238,46 @@ export class ModalEditor extends CustomEditor { } } + private shouldBypassReleaseFilterForInsertPaste(data: string): boolean { + if (this.mode !== "insert" || this.pendingExCommand !== null) return false; + + if (this.forwardingBracketedPasteInInsertMode) { + if (data.includes(BRACKETED_PASTE_END)) { + this.forwardingBracketedPasteInInsertMode = false; + } + // TODO(TODO-23f57080): this only bypasses release filtering. A split + // paste terminator (`"\x1b"` then `"[201~"`) can still be handled as + // Escape below and leave INSERT mode; tracked separately. + return true; + } + + const start = data.indexOf(BRACKETED_PASTE_START); + if (start === -1) return false; + + const afterStart = data.slice(start + BRACKETED_PASTE_START.length); + if (!afterStart.includes(BRACKETED_PASTE_END)) { + this.forwardingBracketedPasteInInsertMode = true; + } + return true; + } + handleInput(data: string): void { this.ensureOnChangeHook(); - if (this.pendingExCommand !== null) { - const normalized = this.normalizePendingExCommandInput(data); - if (normalized === null) return; - data = normalized; + const segments = this.normalizePendingExCommandInput(data); + if (segments === null) return; + for (const segment of segments) { + if (!segment.pasted) { + if (isKeyReleaseEvent(segment.text)) continue; + if (this.isEscapeLikeInput(segment.text)) { + this.handleEscape(); + return; + } + } + this.handlePendingExCommand(segment.text, segment.pasted); + if (this.pendingExCommand === null) return; + } + return; } else if (this.mode !== "insert") { if (this.discardingBracketedPasteInNormalMode) { if (this.isEscapeLikeInput(data)) { @@ -949,34 +1314,37 @@ export class ModalEditor extends CustomEditor { data = filtered; } + const allowInsertPastePayload = this.shouldBypassReleaseFilterForInsertPaste(data); + const delegateWantsInsertKeyRelease = this.mode === "insert" + && this.insertDelegate?.wantsKeyRelease === true; + if (!allowInsertPastePayload + && !delegateWantsInsertKeyRelease + && isKeyReleaseEvent(data) + ) return; + if (this.isEscapeLikeInput(data)) { this.handleEscape(); return; } if (this.mode === "insert") { - // Shift+Alt+A: go to end of line (like Esc -> A but stay in insert) if (matchesKey(data, Key.shiftAlt("a")) || data === "\x1bA") { - super.handleInput(CTRL_E); + this.applyEditorPrimitive(CTRL_E); return; } - // Shift+Alt+I: go to start of line (like Esc -> I but stay in insert) if (matchesKey(data, Key.shiftAlt("i")) || data === "\x1bI") { - super.handleInput(CTRL_A); + this.applyEditorPrimitive(CTRL_A); return; } - // Alt+o: open new line below (stay in insert mode) if (matchesKey(data, Key.alt("o")) || data === "\x1bo") { this.openLineBelow(); return; } - // Alt+Shift+o: open new line above (stay in insert mode) - // \x1bO is the legacy sequence for Alt+Shift+O (VT100 SS3 prefix in non-Kitty terminals) if (matchesKey(data, Key.shiftAlt("o")) || data === "\x1bO") { this.openLineAbove(); return; } - super.handleInput(data); + this.handleInsertInput(data); return; } @@ -1039,8 +1407,60 @@ export class ModalEditor extends CustomEditor { this.handleNormalMode(data); } + private handleInsertInput(data: string): void { this.applyEditorPrimitive(data); } + + private applyEditorPrimitive(data: string): void { + // Raw NORMAL/EX input stays in pi-vim. The editor primitives produced by + // those commands still target the delegate when present so delegate-owned + // undo stacks and sidecar state stay coherent; see pi-vim.spec ADR 0009. + this.syncInsertDelegate(); + const editor = this.insertDelegate; + if (editor) editor.handleInput(data); else super.handleInput(data); + } + + private syncInsertDelegate(): void { + const editor = this.insertDelegate; + if (!editor) return; + + const handlerSurface = editor as CustomEditorHandlerSurface; + const ownedHandlers = this.ownedDelegateHandlers.get(editor) ?? {}; + + syncVoid( + handlerSurface, + ownedHandlers, + "onEscape", + this.onEscape ?? this.actionHandlers.get("app.interrupt"), + ); + syncVoid( + handlerSurface, + ownedHandlers, + "onCtrlD", + this.onCtrlD ?? this.actionHandlers.get("app.exit"), + ); + syncVoid(handlerSurface, ownedHandlers, "onPasteImage", this.onPasteImage); + syncExt(handlerSurface, ownedHandlers, this.onExtensionShortcut); + this.ownedDelegateHandlers.set(editor, ownedHandlers); + + if (!(handlerSurface.actionHandlers instanceof Map)) { + handlerSurface.actionHandlers = new Map(); + } + // Outer app actions intentionally replace same-key delegate actions; + // syncActionHandlers only avoids stale copied entries. See pi-vim.spec + // ADR 0008. + syncActionHandlers(handlerSurface.actionHandlers, ownedHandlers, this.actionHandlers); + + syncText(handlerSurface, ownedHandlers, "onSubmit", this.onSubmit); + syncText(handlerSurface, ownedHandlers, "onChange", this.onChange); + + Object.assign(editor, { + borderColor: this.borderColor, + focused: this.focused, + disableSubmit: this.disableSubmit, + }); + } + private clearUnderlyingPasteStateIfActive(): void { - const editor = this as unknown as { + const editor = this.primitiveEditor() as ModalEditorInternals & { isInPaste?: boolean; pasteBuffer?: string; pasteCounter?: number; @@ -1077,10 +1497,11 @@ export class ModalEditor extends CustomEditor { return; } if (this.mode === "insert") { + this.forwardingBracketedPasteInInsertMode = false; this.clearUnderlyingPasteStateIfActive(); this.mode = "normal"; } else { - super.handleInput("\x1b"); // pass escape to abort agent + this.applyEditorPrimitive("\x1b"); } } @@ -1110,9 +1531,10 @@ export class ModalEditor extends CustomEditor { this.pendingExCommand = current.slice(0, previousGrapheme.end); } - private handlePendingExCommandControlChunk(data: string): boolean { + private handlePendingExCommandControlChunk(data: string, allowLiteralControls = false): boolean { if ( - !data.includes("\r") + !allowLiteralControls + && !data.includes("\r") && !data.includes("\n") && !data.includes("\x7f") && !data.includes("\x08") @@ -1145,6 +1567,10 @@ export class ModalEditor extends CustomEditor { const codePoint = char.codePointAt(0); if (codePoint === undefined || codePoint < 32 || codePoint === 127) { + if (allowLiteralControls) { + printable += char; + continue; + } this.clearPendingExCommand(); return true; } @@ -1156,7 +1582,7 @@ export class ModalEditor extends CustomEditor { return true; } - private handlePendingExCommand(data: string): void { + private handlePendingExCommand(data: string, allowLiteralControls = false): void { if (this.isEnterLikeInput(data)) { this.submitPendingExCommand(); return; @@ -1167,7 +1593,7 @@ export class ModalEditor extends CustomEditor { return; } - if (this.handlePendingExCommandControlChunk(data)) { + if (this.handlePendingExCommandControlChunk(data, allowLiteralControls)) { return; } @@ -1204,7 +1630,7 @@ export class ModalEditor extends CustomEditor { } if (command) { - this.notifyFn(`Unsupported ex command: :${command}`); + this.notifyFn(`Unsupported ex command: :${escapeTerminalControls(command)}`); } } @@ -1266,7 +1692,7 @@ export class ModalEditor extends CustomEditor { this.prefixCount = ""; this.operatorCount = ""; if (!this.isPrintableChunk(data)) { - super.handleInput(data); + this.applyEditorPrimitive(data); } } @@ -1438,8 +1864,6 @@ export class ModalEditor extends CustomEditor { const supportsCountedTextObject = data === "i" || data === "a"; if (hasCount && !supportsCountedWordMotion && !supportsCountedTextObject) { - // Counted forms beyond dd, d{count}j/k, d{count}{f/F/t/T}, and - // d{count}{w/e/b/W/E/B}/{i/a}w are out of scope. this.cancelPendingOperator(data); return; } @@ -1455,7 +1879,6 @@ export class ModalEditor extends CustomEditor { return; } - // Invalid motion: cancel operator to avoid sticky surprising deletes. this.cancelPendingOperator(data); } @@ -1541,7 +1964,6 @@ export class ModalEditor extends CustomEditor { return; } - // Invalid motion: cancel operator to avoid sticky surprising changes. this.cancelPendingOperator(data); } @@ -1666,7 +2088,6 @@ export class ModalEditor extends CustomEditor { && !supportsCountedParagraphMotion && !supportsCountedUnderscore ) { - // Unsupported prefixed forms: drop count and keep processing this key. this.prefixCount = ""; this.operatorCount = ""; } @@ -1810,20 +2231,19 @@ export class ModalEditor extends CustomEditor { return; } - // Pass control sequences (ctrl+c, etc.) to super, ignore printable chars if (this.isPrintableChunk(data)) return; - super.handleInput(data); + this.applyEditorPrimitive(data); } private openLineBelow(): void { - super.handleInput(CTRL_E); - super.handleInput(NEWLINE); + this.applyEditorPrimitive(CTRL_E); + this.applyEditorPrimitive(NEWLINE); } private openLineAbove(): void { - super.handleInput(CTRL_A); - super.handleInput(NEWLINE); - super.handleInput(ESC_UP); + this.applyEditorPrimitive(CTRL_A); + this.applyEditorPrimitive(NEWLINE); + this.applyEditorPrimitive(ESC_UP); } private handleMappedKey(key: string): void { @@ -1835,12 +2255,12 @@ export class ModalEditor extends CustomEditor { case "a": this.mode = "insert"; if (!this.isCursorAtOrPastEol()) { - super.handleInput(ESC_RIGHT); + this.applyEditorPrimitive(ESC_RIGHT); } break; case "A": this.mode = "insert"; - super.handleInput(CTRL_E); + this.applyEditorPrimitive(CTRL_E); break; case "I": this.mode = "insert"; @@ -1882,7 +2302,7 @@ export class ModalEditor extends CustomEditor { this.moveCursorVertically(-1); break; default: - if (seq) super.handleInput(seq); + if (seq) this.applyEditorPrimitive(seq); } } @@ -1912,11 +2332,7 @@ export class ModalEditor extends CustomEditor { private tryMoveCursorByState(delta: number): boolean { if (delta === 0) return true; - const editor = this as unknown as { - state?: { lines?: string[]; cursorLine?: number; cursorCol?: number }; - preferredVisualCol?: number; - tui?: { requestRender?: () => void }; - }; + const editor = this.primitiveEditor(); const state = editor.state; if (!state || !Array.isArray(state.lines)) return false; @@ -1929,8 +2345,6 @@ export class ModalEditor extends CustomEditor { const target = cursorCol + delta; - // Only short-circuit line-local movement when each grapheme is one code - // unit; otherwise let the base editor keep cursor boundaries valid. if (target < 0 || target > line.length) return false; state.cursorCol = target; @@ -1946,25 +2360,20 @@ export class ModalEditor extends CustomEditor { const seq = delta > 0 ? ESC_RIGHT : ESC_LEFT; for (let i = 0; i < Math.abs(delta); i++) { - super.handleInput(seq); + this.applyEditorPrimitive(seq); } } private moveCursorVertically(delta: number): void { if (delta === 0) return; - const editor = this as unknown as { - state?: { lines?: string[]; cursorLine?: number; cursorCol?: number }; - preferredVisualCol?: number | null; - lastAction?: string | null; - tui?: { requestRender?: () => void }; - }; + const editor = this.primitiveEditor(); const state = editor.state; if (!state || !Array.isArray(state.lines) || state.lines.length === 0) { const seq = delta > 0 ? ESC_DOWN : ESC_UP; for (let i = 0; i < Math.abs(delta); i++) { - super.handleInput(seq); + this.applyEditorPrimitive(seq); } return; } @@ -1983,12 +2392,7 @@ export class ModalEditor extends CustomEditor { } private moveCursorToCol(col: number): void { - const editor = this as unknown as { - state?: { lines?: string[]; cursorLine?: number; cursorCol?: number }; - preferredVisualCol?: number | null; - lastAction?: string | null; - tui?: { requestRender?: () => void }; - }; + const editor = this.primitiveEditor(); const state = editor.state; if (!state || !Array.isArray(state.lines)) return; @@ -2000,12 +2404,7 @@ export class ModalEditor extends CustomEditor { } private moveCursorToAbsoluteIndex(abs: number): void { - const editor = this as unknown as { - state?: { lines?: string[]; cursorLine?: number; cursorCol?: number }; - preferredVisualCol?: number | null; - lastAction?: string | null; - tui?: { requestRender?: () => void }; - }; + const editor = this.primitiveEditor(); const state = editor.state; if (!state || !Array.isArray(state.lines)) return; @@ -2019,16 +2418,11 @@ export class ModalEditor extends CustomEditor { } private moveCursorToLineStart(lineIndex: number): void { - const editor = this as unknown as { - state?: { lines?: string[]; cursorLine?: number; cursorCol?: number }; - preferredVisualCol?: number | null; - lastAction?: string | null; - tui?: { requestRender?: () => void }; - }; + const editor = this.primitiveEditor(); const state = editor.state; if (!state || !Array.isArray(state.lines) || state.lines.length === 0) { - super.handleInput(CTRL_A); + this.applyEditorPrimitive(CTRL_A); return; } @@ -2057,7 +2451,7 @@ export class ModalEditor extends CustomEditor { if (steps === 0) return; this.applySyntheticEdit(() => { - const editor = this as unknown as ModalEditorInternals; + const editor = this.primitiveEditor(); const state = editor.state; if (!state || !Array.isArray(state.lines)) return; @@ -2441,7 +2835,7 @@ export class ModalEditor extends CustomEditor { const deleted = col < line.length ? line.slice(col) : hasNextLine ? "\n" : ""; this.writeToRegister(deleted); - super.handleInput(CTRL_K); + this.applyEditorPrimitive(CTRL_K); } private cutCurrentLineContent(): void { @@ -2453,8 +2847,8 @@ export class ModalEditor extends CustomEditor { const deleted = line.length > 0 ? line : hasNextLine ? "\n" : ""; this.writeToRegister(deleted); - super.handleInput(CTRL_A); - super.handleInput(CTRL_K); + this.applyEditorPrimitive(CTRL_A); + this.applyEditorPrimitive(CTRL_K); } private cutLine(): void { @@ -2514,8 +2908,7 @@ export class ModalEditor extends CustomEditor { const newText = text.slice(0, startAbs) + text.slice(endAbs); this.replaceTextInBuffer(newText, startAbs); - // Ensure cursor is at column 0 of the landing line - super.handleInput(CTRL_A); + this.applyEditorPrimitive(CTRL_A); } } @@ -2547,7 +2940,6 @@ export class ModalEditor extends CustomEditor { const col = cursor.col; if (motion === "$") { - // Match D/C behavior exactly, including newline kill at EOL. this.cutToEndOfLine(); return true; } @@ -2604,7 +2996,7 @@ export class ModalEditor extends CustomEditor { if (targetCol === null) return; this.lastCharMotion = { motion, char: targetChar }; - this.deleteRange(col, targetCol, true); // char motions are inclusive + this.deleteRange(col, targetCol, true); } private handlePendingYank(data: string): void { @@ -2665,7 +3057,6 @@ export class ModalEditor extends CustomEditor { } if (this.hasPendingCount()) { - // Counted forms beyond yy, y{count}j/k, and y{count}{f/F/t/T} are out of scope. this.cancelPendingOperator(data); return; } @@ -2673,7 +3064,7 @@ export class ModalEditor extends CustomEditor { if (this.yankWithMotion(data)) { this.pendingOperator = null; } else { - this.cancelPendingOperator(data); // cancel on unrecognised motion + this.cancelPendingOperator(data); } } @@ -2739,7 +3130,7 @@ export class ModalEditor extends CustomEditor { if (targetCol === null) return; this.lastCharMotion = { motion, char: targetChar }; - this.yankRange(col, targetCol, true); // char motions are inclusive + this.yankRange(col, targetCol, true); } private yankRange(col: number, targetCol: number, inclusive: boolean): void { @@ -2755,7 +3146,6 @@ export class ModalEditor extends CustomEditor { if (end <= start) return; - // Yank only — no cursor movement, no text mutation this.writeToRegister(line.slice(start, end), "yank"); } @@ -2781,7 +3171,7 @@ export class ModalEditor extends CustomEditor { } private replaceTextInBuffer(text: string, cursorAbs: number): void { - const editor = this as unknown as { + const editor = this.primitiveEditor() as ModalEditorInternals & { state?: { lines?: string[]; cursorLine?: number; cursorCol?: number }; preferredVisualCol?: number | null; historyIndex?: number; @@ -2804,7 +3194,8 @@ export class ModalEditor extends CustomEditor { state.cursorLine = line; state.cursorCol = col; editor.preferredVisualCol = null; - editor.onChange?.(text); + const synced = this.syncDelegateTextAfterDirectMutation(editor, text, { line, col }); + if (!synced) editor.onChange?.(text); if (editor.autocompleteState) editor.updateAutocomplete?.(); editor.tui?.requestRender?.(); } @@ -2842,7 +3233,7 @@ export class ModalEditor extends CustomEditor { ); } - private static readonly PUT_SIZE_LIMIT = 512 * 1024; // 512 KB safety cap + private static readonly PUT_SIZE_LIMIT = 512 * 1024; private getPasteRegisterText(): string { if (this.clipboardMirror.hasPendingWrite()) { @@ -2866,23 +3257,21 @@ export class ModalEditor extends CustomEditor { if (text.endsWith("\n")) { const content = text.slice(0, -1); for (let i = 0; i < safeCount; i++) { - // Line-wise: insert new line below and fill it - super.handleInput(CTRL_E); - super.handleInput(NEWLINE); + this.applyEditorPrimitive(CTRL_E); + this.applyEditorPrimitive(NEWLINE); for (const char of content) { - super.handleInput(char === "\n" ? NEWLINE : char); + this.applyEditorPrimitive(char === "\n" ? NEWLINE : char); } } return; } - // Character-wise: insert after cursor if (!this.isCursorAtOrPastEol()) { - super.handleInput(ESC_RIGHT); + this.applyEditorPrimitive(ESC_RIGHT); } for (let i = 0; i < safeCount; i++) { for (const char of text) { - super.handleInput(char === "\n" ? NEWLINE : char); + this.applyEditorPrimitive(char === "\n" ? NEWLINE : char); } } } @@ -2896,21 +3285,19 @@ export class ModalEditor extends CustomEditor { if (text.endsWith("\n")) { const content = text.slice(0, -1); for (let i = 0; i < safeCount; i++) { - // Line-wise: insert new line above and fill it - super.handleInput(CTRL_A); - super.handleInput(NEWLINE); - super.handleInput(ESC_UP); + this.applyEditorPrimitive(CTRL_A); + this.applyEditorPrimitive(NEWLINE); + this.applyEditorPrimitive(ESC_UP); for (const char of content) { - super.handleInput(char === "\n" ? NEWLINE : char); + this.applyEditorPrimitive(char === "\n" ? NEWLINE : char); } } return; } - // Character-wise: insert before cursor (just type it) for (let i = 0; i < safeCount; i++) { for (const char of text) { - super.handleInput(char === "\n" ? NEWLINE : char); + this.applyEditorPrimitive(char === "\n" ? NEWLINE : char); } } } @@ -3011,7 +3398,14 @@ export class ModalEditor extends CustomEditor { } render(width: number): string[] { - const lines = super.render(width); + const delegate = this.insertDelegate; + let lines: string[]; + if (delegate) { + this.syncInsertDelegate(); + lines = [...delegate.render(width)]; + } else { + lines = super.render(width); + } this.syncCursorShapeForRender(lines); if (lines.length === 0) return lines; @@ -3036,7 +3430,7 @@ export class ModalEditor extends CustomEditor { private getModeLabel(): string { if (this.mode === "insert") return " INSERT "; - if (this.pendingExCommand !== null) return ` EX ${this.pendingExCommand}_ `; + if (this.pendingExCommand !== null) return ` EX ${escapeTerminalControls(this.pendingExCommand)}_ `; const prefixCount = this.prefixCount; const operatorCount = this.operatorCount; @@ -3061,8 +3455,21 @@ export class ModalEditor extends CustomEditor { if (count) return ` NORMAL ${count}_ `; return " NORMAL "; } + }; } +type CustomEditorConstructorArgs = ConstructorParameters; +const ModalEditorClass = createModalEditor(CustomEditor); +type ModalEditorInstance = InstanceType; + +export const ModalEditor = ModalEditorClass as new ( + tui: CustomEditorConstructorArgs[0], + theme: CustomEditorConstructorArgs[1], + keybindings: CustomEditorConstructorArgs[2], + optionsOrColorizers?: CustomEditorConstructorArgs[3] | ModeLabelColorizers | null, +) => ModalEditorInstance; +export type ModalEditor = ModalEditorInstance; + export default function (pi: ExtensionAPI) { let cursorShapeCleanup: CursorShapeCleanup | null = null; @@ -3080,12 +3487,40 @@ export default function (pi: ExtensionAPI) { normal: (s: string) => t.fg("borderAccent", reverseVideo(s)), ex: (s: string) => t.fg("warning", reverseVideo(s)), } : null; + // Pi clears the editor factory before re-emitting session_start on reload + // or session replacement. Trust that contract instead of tagging pi-vim + // factories; see pi-vim.spec ADR 0007. + const previousFactory = ctx.ui.getEditorComponent?.(); + ctx.ui.setEditorComponent((tui, theme, kb) => { cursorShapeCleanup = enableCursorShapeSupport(tui); - const editor = new ModalEditor(tui, theme, kb, colorizers); + + const editor = new ModalEditor(tui, theme, kb); + editor.setColorizers(colorizers); editor.setClipboardMirrorPolicy(clipboardMirrorPolicy.policy); editor.setQuitFn(() => ctx.shutdown()); editor.setNotifyFn((message) => ctx.ui.notify(message, "warning")); + + if (previousFactory) { + try { + const previousEditor = previousFactory(tui, theme, kb); + const compatibility = getCompatibility(previousEditor); + if (compatibility.compatible) { + editor.setInsertDelegate(compatibility.editor); + } else { + ctx.ui.notify( + `incompatible previous editor (${compatibility.reason})`, + "warning", + ); + } + } catch (error) { + ctx.ui.notify( + `previous editor factory failed (${formatUnknownError(error)})`, + "warning", + ); + } + } + return editor; }); }); diff --git a/package-lock.json b/package-lock.json index 39a8e068..0955958d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "devDependencies": { "@biomejs/biome": "2.4.8", "@eslint/js": "^9.25.1", + "@mariozechner/pi-coding-agent": "^0.71.0", "@types/node": "^24.7.2", "eslint": "^9.25.1", "lefthook": "^2.1.4", @@ -24,11 +25,11 @@ } }, "node_modules/@anthropic-ai/sdk": { - "version": "0.73.0", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.73.0.tgz", - "integrity": "sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw==", + "version": "0.91.1", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", + "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "json-schema-to-ts": "^3.1.1" }, @@ -48,8 +49,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -63,8 +64,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", @@ -79,8 +80,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -92,8 +93,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -106,8 +107,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -120,8 +121,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", @@ -135,8 +136,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" } @@ -145,8 +146,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", @@ -157,8 +158,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -170,8 +171,8 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" @@ -184,8 +185,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" @@ -195,58 +196,58 @@ } }, "node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.998.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.998.0.tgz", - "integrity": "sha512-orRgpdNmdRLik+en3xDxlGuT5AxQU+GFUTMn97ZdRuPLnAiY7Y6/8VTsod6y97/3NB8xuTZbH9wNXzW97IWNMA==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1040.0.tgz", + "integrity": "sha512-tFCqtci1gVGIRwgK3tmv2DV2EawXjBIQgwM/7KaeL4wHUMhNMUA+POUw6vGowtQb51ZaSDjK3KzI3MaQskOyuw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/credential-provider-node": "^3.972.13", - "@aws-sdk/eventstream-handler-node": "^3.972.8", - "@aws-sdk/middleware-eventstream": "^3.972.5", - "@aws-sdk/middleware-host-header": "^3.972.5", - "@aws-sdk/middleware-logger": "^3.972.5", - "@aws-sdk/middleware-recursion-detection": "^3.972.5", - "@aws-sdk/middleware-user-agent": "^3.972.14", - "@aws-sdk/middleware-websocket": "^3.972.9", - "@aws-sdk/region-config-resolver": "^3.972.5", - "@aws-sdk/token-providers": "3.998.0", - "@aws-sdk/types": "^3.973.3", - "@aws-sdk/util-endpoints": "^3.996.2", - "@aws-sdk/util-user-agent-browser": "^3.972.5", - "@aws-sdk/util-user-agent-node": "^3.972.13", - "@smithy/config-resolver": "^4.4.9", - "@smithy/core": "^3.23.6", - "@smithy/eventstream-serde-browser": "^4.2.10", - "@smithy/eventstream-serde-config-resolver": "^4.3.10", - "@smithy/eventstream-serde-node": "^4.2.10", - "@smithy/fetch-http-handler": "^5.3.11", - "@smithy/hash-node": "^4.2.10", - "@smithy/invalid-dependency": "^4.2.10", - "@smithy/middleware-content-length": "^4.2.10", - "@smithy/middleware-endpoint": "^4.4.20", - "@smithy/middleware-retry": "^4.4.37", - "@smithy/middleware-serde": "^4.2.11", - "@smithy/middleware-stack": "^4.2.10", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/node-http-handler": "^4.4.12", - "@smithy/protocol-http": "^5.3.10", - "@smithy/smithy-client": "^4.12.0", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.10", - "@smithy/util-base64": "^4.3.1", - "@smithy/util-body-length-browser": "^4.2.1", - "@smithy/util-body-length-node": "^4.2.2", - "@smithy/util-defaults-mode-browser": "^4.3.36", - "@smithy/util-defaults-mode-node": "^4.2.39", - "@smithy/util-endpoints": "^3.3.1", - "@smithy/util-middleware": "^4.2.10", - "@smithy/util-retry": "^4.2.10", - "@smithy/util-stream": "^4.5.15", - "@smithy/util-utf8": "^4.2.1", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/credential-provider-node": "^3.972.38", + "@aws-sdk/eventstream-handler-node": "^3.972.14", + "@aws-sdk/middleware-eventstream": "^3.972.10", + "@aws-sdk/middleware-host-header": "^3.972.10", + "@aws-sdk/middleware-logger": "^3.972.10", + "@aws-sdk/middleware-recursion-detection": "^3.972.11", + "@aws-sdk/middleware-user-agent": "^3.972.37", + "@aws-sdk/middleware-websocket": "^3.972.16", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/token-providers": "3.1040.0", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@aws-sdk/util-user-agent-browser": "^3.972.10", + "@aws-sdk/util-user-agent-node": "^3.973.23", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.17", + "@smithy/eventstream-serde-browser": "^4.2.14", + "@smithy/eventstream-serde-config-resolver": "^4.3.14", + "@smithy/eventstream-serde-node": "^4.2.14", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/hash-node": "^4.2.14", + "@smithy/invalid-dependency": "^4.2.14", + "@smithy/middleware-content-length": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-retry": "^4.5.7", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.49", + "@smithy/util-defaults-mode-node": "^4.2.54", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.6", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -254,24 +255,25 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.973.14", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.14.tgz", - "integrity": "sha512-iAQ1jIGESTVjoqNNY9VlsE9FnCz+Hc8s+dgurF6WrgFyVIw+uggH+V102RFhwjRv4dLSSLfzjDwvQnLszov7TQ==", + "version": "3.974.7", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.7.tgz", + "integrity": "sha512-YhRC90ofz5oolTJZlA8voU/oUrCB2azi8Usx51k8hhB5LpWbYQMMXKUqSqkoL0Cru+RQJgWTHpAfEDDIwfUhJw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@aws-sdk/xml-builder": "^3.972.7", - "@smithy/core": "^3.23.6", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/property-provider": "^4.2.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/signature-v4": "^5.3.10", - "@smithy/smithy-client": "^4.12.0", - "@smithy/types": "^4.13.0", - "@smithy/util-base64": "^4.3.1", - "@smithy/util-middleware": "^4.2.10", - "@smithy/util-utf8": "^4.2.1", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/xml-builder": "^3.972.22", + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.6", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -279,16 +281,16 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.12.tgz", - "integrity": "sha512-WPtj/iAYHHd+NDM6AZoilZwUz0nMaPxbTPGLA7nhyIYRZN2L8trqfbNvm7g/Jr3gzfKp1LpO6AtBTnrhz9WW2g==", + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.33.tgz", + "integrity": "sha512-bJV7eViSJV6GSuuN+VIdNVPdwPsNSf75BiC2v5alPrjR/OCcqgKwSZInKbDFz9mNeizldsyf67jt6YSIiv53Cw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/types": "^3.973.3", - "@smithy/property-provider": "^4.2.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -296,21 +298,21 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.14", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.14.tgz", - "integrity": "sha512-umtjCicH2o/Fcc8Fu1562UkDyt6gql4czTYVlUfHfAM8S4QEKggzmtHYYYpPfQcjFj1ajyy68ahYSuF67x4ptQ==", + "version": "3.972.35", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.35.tgz", + "integrity": "sha512-x/BQGEIdq0oI+4WxLjKmnQvT7CnF9r8ezdGt7wXwxb7ckHXQz0Zmgxt8v3Ne0JaT3R5YefmuybHX6E8EnsDXyA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/types": "^3.973.3", - "@smithy/fetch-http-handler": "^5.3.11", - "@smithy/node-http-handler": "^4.4.12", - "@smithy/property-provider": "^4.2.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/smithy-client": "^4.12.0", - "@smithy/types": "^4.13.0", - "@smithy/util-stream": "^4.5.15", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/types": "^3.973.8", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-stream": "^4.5.25", "tslib": "^2.6.2" }, "engines": { @@ -318,25 +320,25 @@ } }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.12.tgz", - "integrity": "sha512-qjzgnMl6GIBbVeK74jBqSF07+s6kyeZl5R88qjMs302JlqkxE57jkvflDmZ9I017ffEWqIUa9/M4Hfp28qyu1g==", + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.37.tgz", + "integrity": "sha512-eUTpmWfd/BKsq9medhCRcu+GRAhFP2Zrn7/2jKDHHOOjCkhrMoTp/t4cEthqFoG7gE0VGp5wUxrXTdvBCmSmJg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/credential-provider-env": "^3.972.12", - "@aws-sdk/credential-provider-http": "^3.972.14", - "@aws-sdk/credential-provider-login": "^3.972.12", - "@aws-sdk/credential-provider-process": "^3.972.12", - "@aws-sdk/credential-provider-sso": "^3.972.12", - "@aws-sdk/credential-provider-web-identity": "^3.972.12", - "@aws-sdk/nested-clients": "^3.996.2", - "@aws-sdk/types": "^3.973.3", - "@smithy/credential-provider-imds": "^4.2.10", - "@smithy/property-provider": "^4.2.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/credential-provider-env": "^3.972.33", + "@aws-sdk/credential-provider-http": "^3.972.35", + "@aws-sdk/credential-provider-login": "^3.972.37", + "@aws-sdk/credential-provider-process": "^3.972.33", + "@aws-sdk/credential-provider-sso": "^3.972.37", + "@aws-sdk/credential-provider-web-identity": "^3.972.37", + "@aws-sdk/nested-clients": "^3.997.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -344,19 +346,19 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.12.tgz", - "integrity": "sha512-AO57y46PzG24bJzxWLk+FYJG6MzxvXoFXnOKnmKUGV43ub4/FS/4Rz7zCC6ThqUotgqEFd30l5LTAd65RP65pg==", + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.37.tgz", + "integrity": "sha512-Ty68y8ISSC+g5Q3D0K8uAaoINwvfaOslnNpsF/LgVUxyosYXHawcK2yV4HLXDVugiTTYLQfJfcw0ce5meAGkKw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/nested-clients": "^3.996.2", - "@aws-sdk/types": "^3.973.3", - "@smithy/property-provider": "^4.2.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/nested-clients": "^3.997.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -364,23 +366,23 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.13.tgz", - "integrity": "sha512-ME2sgus+gFRtiudy5Xqj9iT/tj8lHOIGrFgktuO5skJU4EngOvTZ1Hpj8mknrW4FgWXmpWhc88NtEscUuuDpKw==", + "version": "3.972.38", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.38.tgz", + "integrity": "sha512-BQ9XYnBDVxR2HuV5huXYQYF/PZMTsY+EnwfGnCU2cA8Zw63XpkOtPY8WqiMIZMQCrKPQQEiFURS/o9CIolRLqg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.12", - "@aws-sdk/credential-provider-http": "^3.972.14", - "@aws-sdk/credential-provider-ini": "^3.972.12", - "@aws-sdk/credential-provider-process": "^3.972.12", - "@aws-sdk/credential-provider-sso": "^3.972.12", - "@aws-sdk/credential-provider-web-identity": "^3.972.12", - "@aws-sdk/types": "^3.973.3", - "@smithy/credential-provider-imds": "^4.2.10", - "@smithy/property-provider": "^4.2.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/credential-provider-env": "^3.972.33", + "@aws-sdk/credential-provider-http": "^3.972.35", + "@aws-sdk/credential-provider-ini": "^3.972.37", + "@aws-sdk/credential-provider-process": "^3.972.33", + "@aws-sdk/credential-provider-sso": "^3.972.37", + "@aws-sdk/credential-provider-web-identity": "^3.972.37", + "@aws-sdk/types": "^3.973.8", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -388,17 +390,17 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.12.tgz", - "integrity": "sha512-msxrHBpVP5AOIDohNPCINUtL47f7XI1TEru3N13uM3nWUMvIRA1vFa8Tlxbxm1EntPPvLAxRmvE5EbjDjOZkbw==", + "version": "3.972.33", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.33.tgz", + "integrity": "sha512-yfjGksI9WQbdMObb0VeLXqzTLI+a0qXLJT9gCDiv0+X/xjPpI3mTz6a5FibrhpuEKIe0gSgvs3MaoFZy5cx4WA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/types": "^3.973.3", - "@smithy/property-provider": "^4.2.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -406,19 +408,38 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.12.tgz", - "integrity": "sha512-D5iC5546hJyhobJN0szOT4KVeJQ8z/meZq2B3lEDZFcvHONKw+tzq36DAJUy3qLTueeB2geSxiHXngQlA11eoA==", + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.37.tgz", + "integrity": "sha512-fpwE+20ntpp3i9Xb9vUuQfXLDKYHH+5I2V+ZG96SX1nBzrruhy10RXDgmN7t1etOz3c55stlA3TeQASUA451NQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/nested-clients": "^3.997.5", + "@aws-sdk/token-providers": "3.1039.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { + "version": "3.1039.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1039.0.tgz", + "integrity": "sha512-NMSFL2HwkAOoCeLCQiqoOq5pT3vVbSjww2QZTuYgYknVwhhv125PSDzZIcL5EYnlxuPWjEOdauZK+FspkZDVdw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/nested-clients": "^3.996.2", - "@aws-sdk/token-providers": "3.998.0", - "@aws-sdk/types": "^3.973.3", - "@smithy/property-provider": "^4.2.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/nested-clients": "^3.997.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -426,18 +447,18 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.12.tgz", - "integrity": "sha512-yluBahBVsduoA/zgV0NAXtwwXvQ6tNn95dNA3Hg+vISdiPWA46QY0d9PLO2KpNbjtm+1oGcWxemS4fYTwJ0W1w==", + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.37.tgz", + "integrity": "sha512-aryawqyebf+3WhAFNHfF62rekFpYtVcVN7dQ89qnAWsa4n5hJst8qBG6gXC24WHtW7Nnhkf9ScYnjwo0Brn3bw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/nested-clients": "^3.996.2", - "@aws-sdk/types": "^3.973.3", - "@smithy/property-provider": "^4.2.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/nested-clients": "^3.997.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -445,15 +466,15 @@ } }, "node_modules/@aws-sdk/eventstream-handler-node": { - "version": "3.972.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.8.tgz", - "integrity": "sha512-tVrf8X7hKnqv3HyVraUbsQW5mfHlD++S5NSIbfQEx0sCRvIwUbTPDl/lJCxhNmZ2zjgUyBIXIKrWilFWBxzv+w==", + "version": "3.972.14", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.14.tgz", + "integrity": "sha512-m4X56gxG76/CKfxNVbOFuYwnAZcHgS6HOH8lgp15HoGHIAVTcZfZrXvcYzJFOMLEJgVn+JHBu6EiNV+xSNXXFg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/eventstream-codec": "^4.2.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/eventstream-codec": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -461,15 +482,15 @@ } }, "node_modules/@aws-sdk/middleware-eventstream": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.5.tgz", - "integrity": "sha512-j8sFerTrzS9tEJhiW2k+T9hsELE+13D5H+mqMjTRyPSgAOebkiK9d4t8vjbLOXuk7yi5lop40x15MubgcjpLmQ==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.10.tgz", + "integrity": "sha512-QUqLs7Af1II9X4fCRAu+EGHG3KHyOp4RkuLhRKoA3NuFlh6TL8i+zXBl8w2LUxqm44B/Kom45hgSlwA1SpTsXQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -477,15 +498,15 @@ } }, "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.5.tgz", - "integrity": "sha512-dVA0m1cEQ2iA6yB19aHvWNeUVTuvTt3AXzT0aiIu2uxk0S7AcmwDCDaRgYa/v+eFHcJVxEnpYTozqA7X62xinw==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.10.tgz", + "integrity": "sha512-IJSsIMeVQ8MMCPbuh1AbltkFhLBLXn7aejzfX5YKT/VLDHn++Dcz8886tXckE+wQssyPUhaXrJhdakO2VilRhg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -493,14 +514,14 @@ } }, "node_modules/@aws-sdk/middleware-logger": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.5.tgz", - "integrity": "sha512-03RqplLZjUTkYi0dDPR/bbOLnDLFNdaVvNENgA3XK7Ph1MhEBhUYlgoGfOyRAKApDZ+WG4ykOoA8jI8J04jmFA==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.10.tgz", + "integrity": "sha512-OOuGvvz1Dm20SjZo5oEBePFqxt5nf8AwkNDSyUHvD9/bfNASmstcYxFAHUowy4n6Io7mWUZ04JURZwSBvyQanQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -508,16 +529,42 @@ } }, "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.5.tgz", - "integrity": "sha512-2QSuuVkpHTe84+mDdnFjHX8rAP3g0yYwLVAhS3lQN1rW5Z/zNsf8/pYQrLjLO4n4sPCsUAkTa0Vrod0lk+o1Tg==", + "version": "3.972.11", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.11.tgz", + "integrity": "sha512-+zz6f79Kj9V5qFK2P+D8Ehjnw4AhphAlCAsPjUqEcInA9umtSSKMrHbSagEeOIsDNuvVrH98bjRHcyQukTrhaQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", + "@aws-sdk/types": "^3.973.8", "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-sdk-s3": { + "version": "3.972.36", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.36.tgz", + "integrity": "sha512-YhPix+0x/MdQrb1Ug1GDKeS5fqylIy+naz800asX8II4jqfTk2KY2KhmmYCwZcky8YWtRQQwWCGdoqeAnip8Uw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-arn-parser": "^3.972.3", + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -525,18 +572,19 @@ } }, "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.972.14", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.14.tgz", - "integrity": "sha512-PzDz+yRAQuIzd+4ZY3s6/TYRzlNKAn4Gae3E5uLV7NnYHqrZHFoAfKE4beXcu3C51pA2/FQ3X2qOGSYqUoN1WQ==", + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.37.tgz", + "integrity": "sha512-N1oNpdiLoVAWYD3WFBnUi3LlfoDA06ZHo4ozyjbsJNLvILzvt//0CnR8N+CZ0NWeYgVB/5V59ivixHCWCx2ALw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/types": "^3.973.3", - "@aws-sdk/util-endpoints": "^3.996.2", - "@smithy/core": "^3.23.6", - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@smithy/core": "^3.23.17", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-retry": "^4.3.6", "tslib": "^2.6.2" }, "engines": { @@ -544,23 +592,23 @@ } }, "node_modules/@aws-sdk/middleware-websocket": { - "version": "3.972.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.9.tgz", - "integrity": "sha512-O+FSwU9UvKd+QNuGLHqvmP33kkH4jh8pAgdMo3wbFLf+u30fS9/2gbSSWWtNCcWkSNFyG6RUlKU7jPSLApFfGw==", + "version": "3.972.16", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.16.tgz", + "integrity": "sha512-86+S9oCyRVGzoMRpQhxkArp7kD2K75GPmaNevd9B6EyNhWoNvnCZZ3WbgN4j7ZT+jvtvBCGZvI2XHsWZJ+BRIg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@aws-sdk/util-format-url": "^3.972.5", - "@smithy/eventstream-codec": "^4.2.10", - "@smithy/eventstream-serde-browser": "^4.2.10", - "@smithy/fetch-http-handler": "^5.3.11", - "@smithy/protocol-http": "^5.3.10", - "@smithy/signature-v4": "^5.3.10", - "@smithy/types": "^4.13.0", - "@smithy/util-base64": "^4.3.1", - "@smithy/util-hex-encoding": "^4.2.1", - "@smithy/util-utf8": "^4.2.1", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-format-url": "^3.972.10", + "@smithy/eventstream-codec": "^4.2.14", + "@smithy/eventstream-serde-browser": "^4.2.14", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -568,49 +616,50 @@ } }, "node_modules/@aws-sdk/nested-clients": { - "version": "3.996.2", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.2.tgz", - "integrity": "sha512-W+u6EM8WRxOIhAhR2mXMHSaUygqItpTehkgxLwJngXqr9RlAR4t6CtECH7o7QK0ct3oyi5Z8ViDHtPbel+D2Rg==", + "version": "3.997.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.5.tgz", + "integrity": "sha512-jGFr6DxtcMTmzOkG/a0jCZYv4BBDmeNYVeO+/memSoDkYCJu4Y58xviYmzwJfYyIVSts+X/BVjJm1uGBnwHEMg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/middleware-host-header": "^3.972.5", - "@aws-sdk/middleware-logger": "^3.972.5", - "@aws-sdk/middleware-recursion-detection": "^3.972.5", - "@aws-sdk/middleware-user-agent": "^3.972.14", - "@aws-sdk/region-config-resolver": "^3.972.5", - "@aws-sdk/types": "^3.973.3", - "@aws-sdk/util-endpoints": "^3.996.2", - "@aws-sdk/util-user-agent-browser": "^3.972.5", - "@aws-sdk/util-user-agent-node": "^3.972.13", - "@smithy/config-resolver": "^4.4.9", - "@smithy/core": "^3.23.6", - "@smithy/fetch-http-handler": "^5.3.11", - "@smithy/hash-node": "^4.2.10", - "@smithy/invalid-dependency": "^4.2.10", - "@smithy/middleware-content-length": "^4.2.10", - "@smithy/middleware-endpoint": "^4.4.20", - "@smithy/middleware-retry": "^4.4.37", - "@smithy/middleware-serde": "^4.2.11", - "@smithy/middleware-stack": "^4.2.10", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/node-http-handler": "^4.4.12", - "@smithy/protocol-http": "^5.3.10", - "@smithy/smithy-client": "^4.12.0", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.10", - "@smithy/util-base64": "^4.3.1", - "@smithy/util-body-length-browser": "^4.2.1", - "@smithy/util-body-length-node": "^4.2.2", - "@smithy/util-defaults-mode-browser": "^4.3.36", - "@smithy/util-defaults-mode-node": "^4.2.39", - "@smithy/util-endpoints": "^3.3.1", - "@smithy/util-middleware": "^4.2.10", - "@smithy/util-retry": "^4.2.10", - "@smithy/util-utf8": "^4.2.1", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/middleware-host-header": "^3.972.10", + "@aws-sdk/middleware-logger": "^3.972.10", + "@aws-sdk/middleware-recursion-detection": "^3.972.11", + "@aws-sdk/middleware-user-agent": "^3.972.37", + "@aws-sdk/region-config-resolver": "^3.972.13", + "@aws-sdk/signature-v4-multi-region": "^3.996.24", + "@aws-sdk/types": "^3.973.8", + "@aws-sdk/util-endpoints": "^3.996.8", + "@aws-sdk/util-user-agent-browser": "^3.972.10", + "@aws-sdk/util-user-agent-node": "^3.973.23", + "@smithy/config-resolver": "^4.4.17", + "@smithy/core": "^3.23.17", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/hash-node": "^4.2.14", + "@smithy/invalid-dependency": "^4.2.14", + "@smithy/middleware-content-length": "^4.2.14", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-retry": "^4.5.7", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-body-length-node": "^4.2.3", + "@smithy/util-defaults-mode-browser": "^4.3.49", + "@smithy/util-defaults-mode-node": "^4.2.54", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.6", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -618,16 +667,34 @@ } }, "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.5.tgz", - "integrity": "sha512-AOitrygDwfTNCLCW7L+GScDy1p49FZ6WutTUFWROouoPetfVNmpL4q8TWD3MhfY/ynhoGhleUQENrBH374EU8w==", + "version": "3.972.13", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.13.tgz", + "integrity": "sha512-CvJ2ZIjK/jVD/lbOpowBVElJyC1YxLTIJ13yM0AEo0t2v7swOzGjSA6lJGH+DwZXQhcjUjoYwc8bVYCX5MDr1A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.973.8", + "@smithy/config-resolver": "^4.4.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.24", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.24.tgz", + "integrity": "sha512-amP7tLikppN940wbBFISYqiuzVmpzMS9U3mcgtmVLjX4fdWI/SNCvrXv6ZxfVzTT4cT0rPKOLhFah2xLwzREWw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/config-resolver": "^4.4.9", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/middleware-sdk-s3": "^3.972.36", + "@aws-sdk/types": "^3.973.8", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -635,18 +702,18 @@ } }, "node_modules/@aws-sdk/token-providers": { - "version": "3.998.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.998.0.tgz", - "integrity": "sha512-JFzi44tQnENZQ+1DYcHfoa/wTRKkccz0VsNMow0rvsxZtqUEkeV2pYFbir35mHTyUKju9995ay1MAGxLt1dpRA==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1040.0.tgz", + "integrity": "sha512-0KTpz2KqASQwzLOywV1bS2TX6Su0bARkATgpSu236BDM/D/6cMQ2EPiFwoRYwwvXsWSDn8KkKp9NV2ZWWA53Xw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/core": "^3.973.14", - "@aws-sdk/nested-clients": "^3.996.2", - "@aws-sdk/types": "^3.973.3", - "@smithy/property-provider": "^4.2.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@aws-sdk/core": "^3.974.7", + "@aws-sdk/nested-clients": "^3.997.5", + "@aws-sdk/types": "^3.973.8", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -654,13 +721,26 @@ } }, "node_modules/@aws-sdk/types": { - "version": "3.973.3", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.3.tgz", - "integrity": "sha512-tma6D8/xHZHJEUqmr6ksZjZ0onyIUqKDQLyp50ttZJmS0IwFYzxBgp5CxFvpYAnah52V3UtgrqGA6E83gtT7NQ==", + "version": "3.973.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", + "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/util-arn-parser": { + "version": "3.972.3", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.972.3.tgz", + "integrity": "sha512-HzSD8PMFrvgi2Kserxuff5VitNq2sgf3w9qxmskKDiDTThWfVteJxuCS9JXiPIPtmCrp+7N9asfIaVhBFORllA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { @@ -668,16 +748,16 @@ } }, "node_modules/@aws-sdk/util-endpoints": { - "version": "3.996.2", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.2.tgz", - "integrity": "sha512-83E6T1CKi0/IozPzqRBKqduW0mS4UQdI3soBH6CG7UgupTADWunqEMOTuPWCs9XGjpJJ4ujj+yu7pn8svhp5yg==", + "version": "3.996.8", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.8.tgz", + "integrity": "sha512-oOZHcRDihk5iEe5V25NVWg45b3qEA8OpHWVdU/XQh8Zj4heVPAJqWvMphQnU7LkufmUo10EpvFPZuQMiFLJK3g==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.10", - "@smithy/util-endpoints": "^3.3.1", + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-endpoints": "^3.4.2", "tslib": "^2.6.2" }, "engines": { @@ -685,15 +765,15 @@ } }, "node_modules/@aws-sdk/util-format-url": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.5.tgz", - "integrity": "sha512-PccfrPQVOEQSL8xaSvu988ESMlqdH1Qfk3AWPZksCOYPHyzYeUV988E+DBachXNV7tBVTUvK85cZYEZu7JtPxQ==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.10.tgz", + "integrity": "sha512-DEKiHNJVtNxdyTeQspzY+15Po/kHm6sF0Cs4HV9Q2+lplB63+DrvdeiSoOSdWEWAoO2RcY1veoXVDz2tWxWCgQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/querystring-builder": "^4.2.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/querystring-builder": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -701,11 +781,11 @@ } }, "node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.4", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.4.tgz", - "integrity": "sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==", + "version": "3.965.5", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", + "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -714,29 +794,30 @@ } }, "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.972.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.5.tgz", - "integrity": "sha512-2ja1WqtuBaEAMgVoHYuWx393DF6ULqdt3OozeO7BosqouYaoU47Adtp9vEF+GImSG/Q8A+dqfwDULTTdMkHGUQ==", + "version": "3.972.10", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.10.tgz", + "integrity": "sha512-FAzqXvfEssGdSIz8ejatan0bOdx1qefBWKF/gWmVBXIP1HkS7v/wjjaqrAGGKvyihrXTXW00/2/1nTJtxpXz7g==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/types": "^3.973.3", - "@smithy/types": "^4.13.0", + "@aws-sdk/types": "^3.973.8", + "@smithy/types": "^4.14.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.972.13", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.972.13.tgz", - "integrity": "sha512-PHErmuu+v6iAST48zcsB2cYwDKW45gk6qCp49t1p0NGZ4EaFPr/tA5jl0X/ekDwvWbuT0LTj++fjjdVQAbuh0Q==", + "version": "3.973.23", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.23.tgz", + "integrity": "sha512-gGwq8L2Euw0aNG6Ey4EktiAo3fSCVoDy1CaBIthd+oeaKHPXUrNaApMewQ6La5Hv0lcznOtECZaNvYyc5LXXfA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@aws-sdk/middleware-user-agent": "^3.972.14", - "@aws-sdk/types": "^3.973.3", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/types": "^4.13.0", + "@aws-sdk/middleware-user-agent": "^3.972.37", + "@aws-sdk/types": "^3.973.8", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -752,14 +833,15 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.7", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.7.tgz", - "integrity": "sha512-9GF86s6mHuc1TYCbuKatMDWl2PyK3KIkpRaI7ul2/gYZPfaLzKZ+ISHhxzVb9KVeakf75tUQe6CXW2gugSCXNw==", + "version": "3.972.22", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.22.tgz", + "integrity": "sha512-PMYKKtJd70IsSG0yHrdAbxBr+ZWBKLvzFZfD3/urxgf6hXVMzuU5M+3MJ5G67RpOmLBu1fAUN65SbWuKUCOlAA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", - "fast-xml-parser": "5.3.6", + "@nodable/entities": "2.1.0", + "@smithy/types": "^4.14.1", + "fast-xml-parser": "5.7.2", "tslib": "^2.6.2" }, "engines": { @@ -767,21 +849,21 @@ } }, "node_modules/@aws/lambda-invoke-store": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", - "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", + "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.0.0" } }, "node_modules/@babel/runtime": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", - "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -855,9 +937,6 @@ "arm64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -875,9 +954,6 @@ "arm64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -895,9 +971,6 @@ "x64" ], "dev": true, - "libc": [ - "glibc" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -915,9 +988,6 @@ "x64" ], "dev": true, - "libc": [ - "musl" - ], "license": "MIT OR Apache-2.0", "optional": true, "os": [ @@ -965,8 +1035,8 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.1.tgz", "integrity": "sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==", + "dev": true, "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" @@ -1655,11 +1725,12 @@ } }, "node_modules/@google/genai": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.43.0.tgz", - "integrity": "sha512-hklCsJNdMlDM1IwcCVcGQFBg2izY0+t5BIGbRsxi2UnKi6AGKL7pqJqmBDNRbw0bYCs4y3NA7TB+fkKfP/Nrdw==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.51.0.tgz", + "integrity": "sha512-vTZZF3CSimN7cn2zsLpW2p5WF0eZa5Gz69ITMPCNHpPrDlAstOfGifSfi0p/s9Z9400f7xJRkgvkQNrcM7pJ6w==", + "dev": true, + "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", @@ -1730,116 +1801,13 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "peer": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT", - "peer": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "peer": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/@mariozechner/clipboard": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.2.tgz", - "integrity": "sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.5.tgz", + "integrity": "sha512-D3F+UrU9CR7roJt0zDLp6Oc+4/KlLDIrN4frH+6V90SJNW2KKUec1oCQIPaaDjCqeOsQyX9dyqYbImIQIM45PA==", + "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">= 10" }, @@ -1863,12 +1831,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1877,12 +1845,12 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.2.tgz", "integrity": "sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==", + "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1894,12 +1862,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1911,12 +1879,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1928,12 +1896,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1945,12 +1913,12 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1962,12 +1930,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1979,12 +1947,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 10" } @@ -1996,12 +1964,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2013,12 +1981,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">= 10" } @@ -2027,8 +1995,8 @@ "version": "2.6.5", "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz", "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "std-env": "^3.10.0", "yoctocolors": "^2.1.2" @@ -2038,36 +2006,35 @@ } }, "node_modules/@mariozechner/pi-agent-core": { - "version": "0.55.1", - "resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.55.1.tgz", - "integrity": "sha512-t9FAb4ouy8HJSIa8gSRC7j8oeUOb2XDdhvBiHj7FhfpYafj1vRPrvGIEXUV8fPJDCI07vhK9iztP27EPk+yEWw==", + "version": "0.71.0", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.71.0.tgz", + "integrity": "sha512-bHO0cfap04IosXNFjK39Kp4Adf5l/MPJDnZcCA0/XUXKAAQFkb9x5j4C8N3/u+JVaXguj+Wjc7IiCLPcrCTjJg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@mariozechner/pi-ai": "^0.55.1" + "@mariozechner/pi-ai": "^0.71.0", + "typebox": "^1.1.24" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@mariozechner/pi-ai": { - "version": "0.55.1", - "resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.55.1.tgz", - "integrity": "sha512-JJX1LrVWPUPMExu0f89XR4nMNP37+FNLjEE4cIHq9Hi6xQtOiiEi7OjDFMx58hWsq81xH1CwmQXqGTWBjbXKTw==", + "version": "0.71.0", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.71.0.tgz", + "integrity": "sha512-Bq5CLfm3tgn1JRHAsX/TVLmCUnLpR0U4smY9WUY3JX3eOKyTgiD27bdZRaoZsUSQp5uk8oBiw8S2mBNqTmZ28g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@anthropic-ai/sdk": "^0.73.0", - "@aws-sdk/client-bedrock-runtime": "^3.983.0", + "@anthropic-ai/sdk": "^0.91.1", + "@aws-sdk/client-bedrock-runtime": "^3.1030.0", "@google/genai": "^1.40.0", - "@mistralai/mistralai": "1.10.0", - "@sinclair/typebox": "^0.34.41", - "ajv": "^8.17.1", - "ajv-formats": "^3.0.1", + "@mistralai/mistralai": "^2.2.0", "chalk": "^5.6.2", - "openai": "6.10.0", + "openai": "6.26.0", "partial-json": "^0.1.7", "proxy-agent": "^6.5.0", + "typebox": "^1.1.24", "undici": "^7.19.1", "zod-to-json-schema": "^3.24.6" }, @@ -2079,16 +2046,16 @@ } }, "node_modules/@mariozechner/pi-coding-agent": { - "version": "0.55.1", - "resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.55.1.tgz", - "integrity": "sha512-H2M8mbBNyDqhON6+3m4H8CjqJ9taGq/CM3B8dG73+VJJIXFm5SExhU9bdgcw2xh0wWj8yEumsj0of6Tu+F7Ffg==", + "version": "0.71.0", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.71.0.tgz", + "integrity": "sha512-e0YRECBYBl3ZwU1KxZwsU2Fd9WH/EDVu/f+pYOf1CoediDmUe4/JeGZSURESej+A6iIxyMtwUdzkPa5KpTmbBA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@mariozechner/jiti": "^2.6.2", - "@mariozechner/pi-agent-core": "^0.55.1", - "@mariozechner/pi-ai": "^0.55.1", - "@mariozechner/pi-tui": "^0.55.1", + "@mariozechner/pi-agent-core": "^0.71.0", + "@mariozechner/pi-ai": "^0.71.0", + "@mariozechner/pi-tui": "^0.71.0", "@silvia-odwyer/photon-node": "^0.3.4", "chalk": "^5.5.0", "cli-highlight": "^2.1.11", @@ -2099,103 +2066,132 @@ "hosted-git-info": "^9.0.2", "ignore": "^7.0.5", "marked": "^15.0.12", - "minimatch": "^10.1.1", + "minimatch": "^10.2.3", "proper-lockfile": "^4.1.2", + "strip-ansi": "^7.1.0", + "typebox": "^1.1.24", + "undici": "^7.19.1", + "uuid": "^14.0.0", "yaml": "^2.8.2" }, "bin": { "pi": "dist/cli.js" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.6.0" }, "optionalDependencies": { - "@mariozechner/clipboard": "^0.3.2" + "@mariozechner/clipboard": "^0.3.5" + } + }, + "node_modules/@mariozechner/pi-coding-agent/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@mariozechner/pi-coding-agent/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/@mariozechner/pi-tui": { - "version": "0.55.1", - "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.55.1.tgz", - "integrity": "sha512-rnqDUp2fm/ySevC0Ltj/ZFRbEc1kZ1A4qHESejj9hA8NVrb/pX9g82XwTE762JOieEGrRWAtmHLNOm7/e4dJMw==", + "version": "0.71.0", + "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.71.0.tgz", + "integrity": "sha512-6GyauY5i+Tp1hWF08OQHM1GfetcjJuQTVRILMdFKs45P7mP5ePOG/P1M0OYYpsrwaRJzPS2Lh2d2e1FweSK+Rg==", "license": "MIT", "peer": true, "dependencies": { "@types/mime-types": "^2.1.4", "chalk": "^5.5.0", "get-east-asian-width": "^1.3.0", - "koffi": "^2.9.0", "marked": "^15.0.12", "mime-types": "^3.0.1" }, "engines": { "node": ">=20.0.0" + }, + "optionalDependencies": { + "koffi": "^2.9.0" } }, "node_modules/@mistralai/mistralai": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.10.0.tgz", - "integrity": "sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==", - "peer": true, + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-2.2.1.tgz", + "integrity": "sha512-uKU8CZmL2RzYKmplsU01hii4p3pe4HqJefpWNRWXm1Tcm0Sm4xXfwSLIy4k7ZCPlbETCGcp69E7hZs+WOJ5itQ==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "zod": "^3.20.0", - "zod-to-json-schema": "^3.24.1" - } - }, - "node_modules/@mistralai/mistralai/node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/colinhacks" + "ws": "^8.18.0", + "zod": "^3.25.0 || ^4.0.0", + "zod-to-json-schema": "^3.25.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=14" - } + "node_modules/@nodable/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", - "license": "BSD-3-Clause", - "peer": true + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" @@ -2205,77 +2201,56 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", - "license": "BSD-3-Clause", - "peer": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.1.tgz", + "integrity": "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", - "license": "BSD-3-Clause", - "peer": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", + "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", - "license": "Apache-2.0", - "peer": true - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", - "license": "MIT", - "peer": true - }, - "node_modules/@smithy/abort-controller": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.10.tgz", - "integrity": "sha512-qocxM/X4XGATqQtUkbE9SPUB6wekBi+FyJOMbPj0AhvyvFGYEmOlz6VB22iMePCQsFmMIvFSeViDvA7mZJG47g==", - "license": "Apache-2.0", - "peer": true, - "dependencies": { - "@smithy/types": "^4.13.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } + "dev": true, + "license": "Apache-2.0" }, "node_modules/@smithy/config-resolver": { - "version": "4.4.9", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.9.tgz", - "integrity": "sha512-ejQvXqlcU30h7liR9fXtj7PIAau1t/sFbJpgWPfiYDs7zd16jpH0IsSXKcba2jF6ChTXvIjACs27kNMc5xxE2Q==", + "version": "4.4.17", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.17.tgz", + "integrity": "sha512-TzDZcAnhTyAHbXVxWZo7/tEcrIeFq20IBk8So3OLOetWpR8EwY/yEqBMBFaJMeyEiREDq4NfEl+qO3OAUD+vbQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.3.10", - "@smithy/types": "^4.13.0", - "@smithy/util-config-provider": "^4.2.1", - "@smithy/util-endpoints": "^3.3.1", - "@smithy/util-middleware": "^4.2.10", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-config-provider": "^4.2.2", + "@smithy/util-endpoints": "^3.4.2", + "@smithy/util-middleware": "^4.2.14", "tslib": "^2.6.2" }, "engines": { @@ -2283,21 +2258,21 @@ } }, "node_modules/@smithy/core": { - "version": "3.23.6", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.6.tgz", - "integrity": "sha512-4xE+0L2NrsFKpEVFlFELkIHQddBvMbQ41LRIP74dGCXnY1zQ9DgksrBcRBDJT+iOzGy4VEJIeU3hkUK5mn06kg==", + "version": "3.23.17", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.17.tgz", + "integrity": "sha512-x7BlLbUFL8NWCGjMF9C+1N5cVCxcPa7g6Tv9B4A2luWx3be3oU8hQ96wIwxe/s7OhIzvoJH73HAUSg5JXVlEtQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/middleware-serde": "^4.2.11", - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", - "@smithy/util-base64": "^4.3.1", - "@smithy/util-body-length-browser": "^4.2.1", - "@smithy/util-middleware": "^4.2.10", - "@smithy/util-stream": "^4.5.15", - "@smithy/util-utf8": "^4.2.1", - "@smithy/uuid": "^1.1.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-body-length-browser": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-stream": "^4.5.25", + "@smithy/util-utf8": "^4.2.2", + "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { @@ -2305,16 +2280,16 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.10.tgz", - "integrity": "sha512-3bsMLJJLTZGZqVGGeBVFfLzuRulVsGTj12BzRKODTHqUABpIr0jMN1vN3+u6r2OfyhAQ2pXaMZWX/swBK5I6PQ==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.14.tgz", + "integrity": "sha512-Au28zBN48ZAoXdooGUHemuVBrkE+Ie6RPmGNIAJsFqj33Vhb6xAgRifUydZ2aY+M+KaMAETAlKk5NC5h1G7wpg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.3.10", - "@smithy/property-provider": "^4.2.10", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.10", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", "tslib": "^2.6.2" }, "engines": { @@ -2322,15 +2297,15 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.10.tgz", - "integrity": "sha512-A4ynrsFFfSXUHicfTcRehytppFBcY3HQxEGYiyGktPIOye3Ot7fxpiy4VR42WmtGI4Wfo6OXt/c1Ky1nUFxYYQ==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.14.tgz", + "integrity": "sha512-erZq0nOIpzfeZdCyzZjdJb4nVSKLUmSkaQUVkRGQTXs30gyUGeKnrYEg+Xe1W5gE3aReS7IgsvANwVPxSzY6Pw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.13.0", - "@smithy/util-hex-encoding": "^4.2.1", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2338,14 +2313,14 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.10.tgz", - "integrity": "sha512-0xupsu9yj9oDVuQ50YCTS9nuSYhGlrwqdaKQel9y2Fz7LU9fNErVlw9N0o4pm4qqvWEGbSTI4HKc6XJfB30MVw==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.14.tgz", + "integrity": "sha512-8IelTCtTctWRbb+0Dcy+C0aICh1qa0qWXqgjcXDmMuCvPJRnv26hiDZoAau2ILOniki65mCPKqOQs/BaWvO4CQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.10", - "@smithy/types": "^4.13.0", + "@smithy/eventstream-serde-universal": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2353,13 +2328,13 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.10.tgz", - "integrity": "sha512-8kn6sinrduk0yaYHMJDsNuiFpXwQwibR7n/4CDUqn4UgaG+SeBHu5jHGFdU9BLFAM7Q4/gvr9RYxBHz9/jKrhA==", + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.14.tgz", + "integrity": "sha512-sqHiHpYRYo3FJlaIxD1J8PhbcmJAm7IuM16mVnwSkCToD7g00IBZzKuiLNMGmftULmEUX6/UAz8/NN5uMP8bVA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2367,14 +2342,14 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.10.tgz", - "integrity": "sha512-uUrxPGgIffnYfvIOUmBM5i+USdEBRTdh7mLPttjphgtooxQ8CtdO1p6K5+Q4BBAZvKlvtJ9jWyrWpBJYzBKsyQ==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.14.tgz", + "integrity": "sha512-Ht/8BuGlKfFTy0H3+8eEu0vdpwGztCnaLLXtpXNdQqiR7Hj4vFScU3T436vRAjATglOIPjJXronY+1WxxNLSiw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.10", - "@smithy/types": "^4.13.0", + "@smithy/eventstream-serde-universal": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2382,14 +2357,14 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.10.tgz", - "integrity": "sha512-aArqzOEvcs2dK+xQVCgLbpJQGfZihw8SD4ymhkwNTtwKbnrzdhJsFDKuMQnam2kF69WzgJYOU5eJlCx+CA32bw==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.14.tgz", + "integrity": "sha512-lWyt4T2XQZUZgK3tQ3Wn0w3XBvZsK/vjTuJl6bXbnGZBHH0ZUSONTYiK9TgjTTzU54xQr3DRFwpjmhp0oLm3gg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/eventstream-codec": "^4.2.10", - "@smithy/types": "^4.13.0", + "@smithy/eventstream-codec": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2397,16 +2372,16 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.11.tgz", - "integrity": "sha512-wbTRjOxdFuyEg0CpumjZO0hkUl+fetJFqxNROepuLIoijQh51aMBmzFLfoQdwRjxsuuS2jizzIUTjPWgd8pd7g==", + "version": "5.3.17", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.17.tgz", + "integrity": "sha512-bXOvQzaSm6MnmLaWA1elgfQcAtN4UP3vXqV97bHuoOrHQOJiLT3ds6o9eo5bqd0TJfRFpzdGnDQdW3FACiAVdw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/protocol-http": "^5.3.10", - "@smithy/querystring-builder": "^4.2.10", - "@smithy/types": "^4.13.0", - "@smithy/util-base64": "^4.3.1", + "@smithy/protocol-http": "^5.3.14", + "@smithy/querystring-builder": "^4.2.14", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" }, "engines": { @@ -2414,15 +2389,15 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.10.tgz", - "integrity": "sha512-1VzIOI5CcsvMDvP3iv1vG/RfLJVVVc67dCRyLSB2Hn9SWCZrDO3zvcIzj3BfEtqRW5kcMg5KAeVf1K3dR6nD3w==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.14.tgz", + "integrity": "sha512-8ZBDY2DD4wr+GGjTpPtiglEsqr0lUP+KHqgZcWczFf6qeZ/YRjMIOoQWVQlmwu7EtxKTd8YXD8lblmYcpBIA1g==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", - "@smithy/util-buffer-from": "^4.2.1", - "@smithy/util-utf8": "^4.2.1", + "@smithy/types": "^4.14.1", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2430,13 +2405,13 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.10.tgz", - "integrity": "sha512-vy9KPNSFUU0ajFYk0sDZIYiUlAWGEAhRfehIr5ZkdFrRFTAuXEPUd41USuqHU6vvLX4r6Q9X7MKBco5+Il0Org==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.14.tgz", + "integrity": "sha512-c21qJiTSb25xvvOp+H2TNZzPCngrvl5vIPqPB8zQ/DmJF4QWXO19x1dWfMJZ6wZuuWUPPm0gV8C0cU3+ifcWuw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2444,11 +2419,11 @@ } }, "node_modules/@smithy/is-array-buffer": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.1.tgz", - "integrity": "sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", + "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2457,14 +2432,14 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.10.tgz", - "integrity": "sha512-TQZ9kX5c6XbjhaEBpvhSvMEZ0klBs1CFtOdPFwATZSbC9UeQfKHPLPN9Y+I6wZGMOavlYTOlHEPDrt42PMSH9w==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.14.tgz", + "integrity": "sha512-xhHq7fX4/3lv5NHxLUk3OeEvl0xZ+Ek3qIbWaCL4f9JwgDZEclPBElljaZCAItdGPQl/kSM4LPMOpy1MYgprpw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2472,19 +2447,19 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.20", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.20.tgz", - "integrity": "sha512-9W6Np4ceBP3XCYAGLoMCmn8t2RRVzuD1ndWPLBbv7H9CrwM9Bprf6Up6BM9ZA/3alodg0b7Kf6ftBK9R1N04vw==", + "version": "4.4.32", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.32.tgz", + "integrity": "sha512-ZZkgyjnJppiZbIm6Qbx92pbXYi1uzenIvGhBSCDlc7NwuAkiqSgS75j1czAD25ZLs2FjMjYy1q7gyRVWG6JA0Q==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/core": "^3.23.6", - "@smithy/middleware-serde": "^4.2.11", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", - "@smithy/url-parser": "^4.2.10", - "@smithy/util-middleware": "^4.2.10", + "@smithy/core": "^3.23.17", + "@smithy/middleware-serde": "^4.2.20", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", + "@smithy/url-parser": "^4.2.14", + "@smithy/util-middleware": "^4.2.14", "tslib": "^2.6.2" }, "engines": { @@ -2492,20 +2467,21 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.37", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.37.tgz", - "integrity": "sha512-/1psZZllBBSQ7+qo5+hhLz7AEPGLx3Z0+e3ramMBEuPK2PfvLK4SrncDB9VegX5mBn+oP/UTDrM6IHrFjvX1ZA==", + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.5.7.tgz", + "integrity": "sha512-bRt6ZImqVSeTk39Nm81K20ObIiAZ3WefY7G6+iz/0tZjs4dgRRjvRX2sgsH+zi6iDCRR/aQvQofLKxxz4rPBZg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.3.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/service-error-classification": "^4.2.10", - "@smithy/smithy-client": "^4.12.0", - "@smithy/types": "^4.13.0", - "@smithy/util-middleware": "^4.2.10", - "@smithy/util-retry": "^4.2.10", - "@smithy/uuid": "^1.1.1", + "@smithy/core": "^3.23.17", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/service-error-classification": "^4.3.1", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-retry": "^4.3.6", + "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { @@ -2513,14 +2489,15 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.11.tgz", - "integrity": "sha512-STQdONGPwbbC7cusL60s7vOa6He6A9w2jWhoapL0mgVjmR19pr26slV+yoSP76SIssMTX/95e5nOZ6UQv6jolg==", + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.20.tgz", + "integrity": "sha512-Lx9JMO9vArPtiChE3wbEZ5akMIDQpWQtlu90lhACQmNOXcGXRbaDywMHDzuDZ2OkZzP+9wQfZi3YJT9F67zTQQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", + "@smithy/core": "^3.23.17", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2528,13 +2505,13 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.10.tgz", - "integrity": "sha512-pmts/WovNcE/tlyHa8z/groPeOtqtEpp61q3W0nW1nDJuMq/x+hWa/OVQBtgU0tBqupeXq0VBOLA4UZwE8I0YA==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.14.tgz", + "integrity": "sha512-2dvkUKLuFdKsCRmOE4Mn63co0Djtsm+JMh0bYZQupN1pJwMeE8FmQmRLLzzEMN0dnNi7CDCYYH8F0EVwWiPBeA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2542,15 +2519,15 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.10", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.10.tgz", - "integrity": "sha512-UALRbJtVX34AdP2VECKVlnNgidLHA2A7YgcJzwSBg1hzmnO/bZBHl/LDQQyYifzUwp1UOODnl9JJ3KNawpUJ9w==", + "version": "4.3.14", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.14.tgz", + "integrity": "sha512-S+gFjyo/weSVL0P1b9Ts8C/CwIfNCgUPikk3sl6QVsfE/uUuO+QsF+NsE/JkpvWqqyz1wg7HFdiaZuj5CoBMRg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/property-provider": "^4.2.10", - "@smithy/shared-ini-file-loader": "^4.4.5", - "@smithy/types": "^4.13.0", + "@smithy/property-provider": "^4.2.14", + "@smithy/shared-ini-file-loader": "^4.4.9", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2558,16 +2535,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.4.12", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.12.tgz", - "integrity": "sha512-zo1+WKJkR9x7ZtMeMDAAsq2PufwiLDmkhcjpWPRRkmeIuOm6nq1qjFICSZbnjBvD09ei8KMo26BWxsu2BUU+5w==", + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.6.1.tgz", + "integrity": "sha512-iB+orM4x3xrr57X3YaXazfKnntl0LHlZB1kcXSGzMV1Tt0+YwEjGlbjk/44qEGtBzXAz6yFDzkYTKSV6Pj2HUg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/abort-controller": "^4.2.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/querystring-builder": "^4.2.10", - "@smithy/types": "^4.13.0", + "@smithy/protocol-http": "^5.3.14", + "@smithy/querystring-builder": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2575,13 +2551,13 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.10.tgz", - "integrity": "sha512-5jm60P0CU7tom0eNrZ7YrkgBaoLFXzmqB0wVS+4uK8PPGmosSrLNf6rRd50UBvukztawZ7zyA8TxlrKpF5z9jw==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.14.tgz", + "integrity": "sha512-WuM31CgfsnQ/10i7NYr0PyxqknD72Y5uMfUMVSniPjbEPceiTErb4eIqJQ+pdxNEAUEWrewrGjIRjVbVHsxZiQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2589,13 +2565,13 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.10.tgz", - "integrity": "sha512-2NzVWpYY0tRdfeCJLsgrR89KE3NTWT2wGulhNUxYlRmtRmPwLQwKzhrfVaiNlA9ZpJvbW7cjTVChYKgnkqXj1A==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.14.tgz", + "integrity": "sha512-dN5F8kHx8RNU0r+pCwNmFZyz6ChjMkzShy/zup6MtkRmmix4vZzJdW+di7x//b1LiynIev88FM18ie+wwPcQtQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2603,14 +2579,14 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.10.tgz", - "integrity": "sha512-HeN7kEvuzO2DmAzLukE9UryiUvejD3tMp9a1D1NJETerIfKobBUCLfviP6QEk500166eD2IATaXM59qgUI+YDA==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.14.tgz", + "integrity": "sha512-XYA5Z0IqTeF+5XDdh4BBmSA0HvbgVZIyv4cmOoUheDNR57K1HgBp9ukUMx3Cr3XpDHHpLBnexPE3LAtDsZkj2A==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", - "@smithy/util-uri-escape": "^4.2.1", + "@smithy/types": "^4.14.1", + "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2618,13 +2594,13 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.10.tgz", - "integrity": "sha512-4Mh18J26+ao1oX5wXJfWlTT+Q1OpDR8ssiC9PDOuEgVBGloqg18Fw7h5Ct8DyT9NBYwJgtJ2nLjKKFU6RP1G1Q==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.14.tgz", + "integrity": "sha512-hr+YyqBD23GVvRxGGrcc/oOeNlK3PzT5Fu4dzrDXxzS1LpFiuL2PQQqKPs87M79aW7ziMs+nvB3qdw77SqE7Lw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2632,26 +2608,26 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.10.tgz", - "integrity": "sha512-0R/+/Il5y8nB/By90o8hy/bWVYptbIfvoTYad0igYQO5RefhNCDmNzqxaMx7K1t/QWo0d6UynqpqN5cCQt1MCg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.3.1.tgz", + "integrity": "sha512-aUQuDGh760ts/8MU+APjIZhlLPKhIIfqyzZaJikLEIMrdxFvxuLYD0WxWzaYWpmLbQlXDe9p7EWM3HsBe0K6Gw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0" + "@smithy/types": "^4.14.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.5.tgz", - "integrity": "sha512-pHgASxl50rrtOztgQCPmOXFjRW+mCd7ALr/3uXNzRrRoGV5G2+78GOsQ3HlQuBVHCh9o6xqMNvlIKZjWn4Euug==", + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.9.tgz", + "integrity": "sha512-495/V2I15SHgedSJoDPD23JuSfKAp726ZI1V0wtjB07Wh7q/0tri/0e0DLefZCHgxZonrGKt/OCTpAtP1wE1kQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2659,19 +2635,19 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.10.tgz", - "integrity": "sha512-Wab3wW8468WqTKIxI+aZe3JYO52/RYT/8sDOdzkUhjnLakLe9qoQqIcfih/qxcF4qWEFoWBszY0mj5uxffaVXA==", + "version": "5.3.14", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.14.tgz", + "integrity": "sha512-1D9Y/nmlVjCeSivCbhZ7hgEpmHyY1h0GvpSZt3l0xcD9JjmjVC1CHOozS6+Gh+/ldMH8JuJ6cujObQqfayAVFA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/is-array-buffer": "^4.2.1", - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", - "@smithy/util-hex-encoding": "^4.2.1", - "@smithy/util-middleware": "^4.2.10", - "@smithy/util-uri-escape": "^4.2.1", - "@smithy/util-utf8": "^4.2.1", + "@smithy/is-array-buffer": "^4.2.2", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-middleware": "^4.2.14", + "@smithy/util-uri-escape": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2679,18 +2655,18 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.0.tgz", - "integrity": "sha512-R8bQ9K3lCcXyZmBnQqUZJF4ChZmtWT5NLi6x5kgWx5D+/j0KorXcA0YcFg/X5TOgnTCy1tbKc6z2g2y4amFupQ==", + "version": "4.12.13", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.13.tgz", + "integrity": "sha512-y/Pcj1V9+qG98gyu1gvftHB7rDpdh+7kIBIggs55yGm3JdtBV8GT8IFF3a1qxZ79QnaJHX9GXzvBG6tAd+czJA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/core": "^3.23.6", - "@smithy/middleware-endpoint": "^4.4.20", - "@smithy/middleware-stack": "^4.2.10", - "@smithy/protocol-http": "^5.3.10", - "@smithy/types": "^4.13.0", - "@smithy/util-stream": "^4.5.15", + "@smithy/core": "^3.23.17", + "@smithy/middleware-endpoint": "^4.4.32", + "@smithy/middleware-stack": "^4.2.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/types": "^4.14.1", + "@smithy/util-stream": "^4.5.25", "tslib": "^2.6.2" }, "engines": { @@ -2698,11 +2674,11 @@ } }, "node_modules/@smithy/types": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.0.tgz", - "integrity": "sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==", + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.1.tgz", + "integrity": "sha512-59b5HtSVrVR/eYNei3BUj3DCPKD/G7EtDDe7OEJE7i7FtQFugYo6MxbotS8mVJkLNVf8gYaAlEBwwtJ9HzhWSg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2711,14 +2687,14 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.10.tgz", - "integrity": "sha512-uypjF7fCDsRk26u3qHmFI/ePL7bxxB9vKkE+2WKEciHhz+4QtbzWiHRVNRJwU3cKhrYDYQE3b0MRFtqfLYdA4A==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.14.tgz", + "integrity": "sha512-p06BiBigJ8bTA3MgnOfCtDUWnAMY0YfedO/GRpmc7p+wg3KW8vbXy1xwSu5ASy0wV7rRYtlfZOIKH4XqfhjSQQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/querystring-parser": "^4.2.10", - "@smithy/types": "^4.13.0", + "@smithy/querystring-parser": "^4.2.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2726,14 +2702,14 @@ } }, "node_modules/@smithy/util-base64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.1.tgz", - "integrity": "sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", + "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/util-buffer-from": "^4.2.1", - "@smithy/util-utf8": "^4.2.1", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2741,11 +2717,11 @@ } }, "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.1.tgz", - "integrity": "sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", + "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2754,11 +2730,11 @@ } }, "node_modules/@smithy/util-body-length-node": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.2.tgz", - "integrity": "sha512-4rHqBvxtJEBvsZcFQSPQqXP2b/yy/YlB66KlcEgcH2WNoOKCKB03DSLzXmOsXjbl8dJ4OEYTn31knhdznwk7zw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", + "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2767,13 +2743,13 @@ } }, "node_modules/@smithy/util-buffer-from": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.1.tgz", - "integrity": "sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", + "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/is-array-buffer": "^4.2.1", + "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2781,11 +2757,11 @@ } }, "node_modules/@smithy/util-config-provider": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.1.tgz", - "integrity": "sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", + "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2794,15 +2770,15 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.36", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.36.tgz", - "integrity": "sha512-R0smq7EHQXRVMxkAxtH5akJ/FvgAmNF6bUy/GwY/N20T4GrwjT633NFm0VuRpC+8Bbv8R9A0DoJ9OiZL/M3xew==", + "version": "4.3.49", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.49.tgz", + "integrity": "sha512-a5bNrdiONYB/qE2BuKegvUMd/+ZDwdg4vsNuuSzYE8qs2EYAdK9CynL+Rzn29PbPiUqoz/cbpRbcLzD5lEevHw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/property-provider": "^4.2.10", - "@smithy/smithy-client": "^4.12.0", - "@smithy/types": "^4.13.0", + "@smithy/property-provider": "^4.2.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2810,18 +2786,18 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.39", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.39.tgz", - "integrity": "sha512-otWuoDm35btJV1L8MyHrPl462B07QCdMTktKc7/yM+Psv6KbED/ziXiHnmr7yPHUjfIwE9S8Max0LO24Mo3ZVg==", + "version": "4.2.54", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.54.tgz", + "integrity": "sha512-g1cvrJvOnzeJgEdf7AE4luI7gp6L8weE0y9a9wQUSGtjb8QRHDbCJYuE4Sy0SD9N8RrnNPFsPltAz/OSoBR9Zw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/config-resolver": "^4.4.9", - "@smithy/credential-provider-imds": "^4.2.10", - "@smithy/node-config-provider": "^4.3.10", - "@smithy/property-provider": "^4.2.10", - "@smithy/smithy-client": "^4.12.0", - "@smithy/types": "^4.13.0", + "@smithy/config-resolver": "^4.4.17", + "@smithy/credential-provider-imds": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/property-provider": "^4.2.14", + "@smithy/smithy-client": "^4.12.13", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2829,14 +2805,14 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.1.tgz", - "integrity": "sha512-xyctc4klmjmieQiF9I1wssBWleRV0RhJ2DpO8+8yzi2LO1Z+4IWOZNGZGNj4+hq9kdo+nyfrRLmQTzc16Op2Vg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.4.2.tgz", + "integrity": "sha512-a55Tr+3OKld4TTtnT+RhKOQHyPxm3j/xL4OR83WBUhLJaKDS9dnJ7arRMOp3t31dcLhApwG9bgvrRXBHlLdIkg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/node-config-provider": "^4.3.10", - "@smithy/types": "^4.13.0", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2844,11 +2820,11 @@ } }, "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.1.tgz", - "integrity": "sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", + "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2857,13 +2833,13 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.10.tgz", - "integrity": "sha512-LxaQIWLp4y0r72eA8mwPNQ9va4h5KeLM0I3M/HV9klmFaY2kN766wf5vsTzmaOpNNb7GgXAd9a25P3h8T49PSA==", + "version": "4.2.14", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.14.tgz", + "integrity": "sha512-1Su2vj9RYNDEv/V+2E+jXkkwGsgR7dc4sfHn9Z7ruzQHJIEni9zzw5CauvRXlFJfmgcqYP8fWa0dkh2Q2YaQyw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/types": "^4.13.0", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2871,14 +2847,14 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.10.tgz", - "integrity": "sha512-HrBzistfpyE5uqTwiyLsFHscgnwB0kgv8vySp7q5kZ0Eltn/tjosaSGGDj/jJ9ys7pWzIP/icE2d+7vMKXLv7A==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.3.6.tgz", + "integrity": "sha512-p6/FO1n2KxMeQyna067i0uJ6TSbb165ZhnRtCpWh4Foxqbfc6oW+XITaL8QkFJj3KFnDe2URt4gOhgU06EP9ew==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/service-error-classification": "^4.2.10", - "@smithy/types": "^4.13.0", + "@smithy/service-error-classification": "^4.3.1", + "@smithy/types": "^4.14.1", "tslib": "^2.6.2" }, "engines": { @@ -2886,19 +2862,19 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.15", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.15.tgz", - "integrity": "sha512-OlOKnaqnkU9X+6wEkd7mN+WB7orPbCVDauXOj22Q7VtiTkvy7ZdSsOg4QiNAZMgI4OkvNf+/VLUC3VXkxuWJZw==", + "version": "4.5.25", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.25.tgz", + "integrity": "sha512-/PFpG4k8Ze8Ei+mMKj3oiPICYekthuzePZMgZbCqMiXIHHf4n2aZ4Ps0aSRShycFTGuj/J6XldmC0x0DwednIA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/fetch-http-handler": "^5.3.11", - "@smithy/node-http-handler": "^4.4.12", - "@smithy/types": "^4.13.0", - "@smithy/util-base64": "^4.3.1", - "@smithy/util-buffer-from": "^4.2.1", - "@smithy/util-hex-encoding": "^4.2.1", - "@smithy/util-utf8": "^4.2.1", + "@smithy/fetch-http-handler": "^5.3.17", + "@smithy/node-http-handler": "^4.6.1", + "@smithy/types": "^4.14.1", + "@smithy/util-base64": "^4.3.2", + "@smithy/util-buffer-from": "^4.2.2", + "@smithy/util-hex-encoding": "^4.2.2", + "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2906,11 +2882,11 @@ } }, "node_modules/@smithy/util-uri-escape": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.1.tgz", - "integrity": "sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", + "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2919,13 +2895,13 @@ } }, "node_modules/@smithy/util-utf8": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.1.tgz", - "integrity": "sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", + "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { - "@smithy/util-buffer-from": "^4.2.1", + "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" }, "engines": { @@ -2933,11 +2909,11 @@ } }, "node_modules/@smithy/uuid": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.1.tgz", - "integrity": "sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", + "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.6.2" }, @@ -2949,8 +2925,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" @@ -2967,15 +2943,15 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", @@ -2995,13 +2971,13 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/node": { "version": "24.12.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "dev": true, "license": "MIT", "dependencies": { "undici-types": "~7.16.0" @@ -3011,16 +2987,16 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@types/node": "*" } @@ -3060,6 +3036,7 @@ "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.58.0", "@typescript-eslint/types": "8.58.0", @@ -3264,6 +3241,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3285,53 +3263,18 @@ "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 14" } }, - "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -3340,6 +3283,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -3355,8 +3299,8 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", @@ -3369,8 +3313,8 @@ "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "tslib": "^2.0.1" }, @@ -3382,6 +3326,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -3391,6 +3336,7 @@ "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, "funding": [ { "type": "github", @@ -3405,15 +3351,14 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/basic-ftp": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", - "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.3.1.tgz", + "integrity": "sha512-bopVNp6ugyA150DDuZfPFdt1KZ5a94ZDiwX4hMgZDzF+GttD80lEy8kj98kbyhLXnPvhtIo93mdnLIjpCAeeOw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" } @@ -3422,8 +3367,8 @@ "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -3432,13 +3377,14 @@ "version": "2.14.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/brace-expansion": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" @@ -3451,8 +3397,8 @@ "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -3461,8 +3407,8 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause", - "peer": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/callsites": { "version": "3.1.0", @@ -3479,7 +3425,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", - "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, @@ -3491,8 +3436,8 @@ "version": "2.1.11", "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", @@ -3513,8 +3458,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3530,8 +3475,8 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", @@ -3542,6 +3487,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -3554,6 +3500,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/concat-map": { @@ -3567,6 +3514,7 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -3581,8 +3529,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 12" } @@ -3591,6 +3539,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3615,8 +3564,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", @@ -3630,25 +3579,18 @@ "version": "8.0.3", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.3.1" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT", - "peer": true - }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "safe-buffer": "^5.0.1" } @@ -3657,15 +3599,15 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "once": "^1.4.0" } @@ -3716,8 +3658,8 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -3739,8 +3681,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", @@ -3763,6 +3705,7 @@ "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3951,8 +3894,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "license": "BSD-2-Clause", - "peer": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -3991,6 +3934,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" @@ -4000,6 +3944,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" @@ -4009,15 +3954,15 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", @@ -4037,6 +3982,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -4053,27 +3999,27 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "node_modules/fast-xml-builder": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.5.tgz", + "integrity": "sha512-4TJn/8FKLeslLAH3dnohXqE3QSoxkhvaMzepOIZytwJXZO69Bfz0HBdDHzOTOon6G59Zrk6VQ2bEiv1t61rfkA==", + "dev": true, "funding": [ { "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" + "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.1.3" + } }, "node_modules/fast-xml-parser": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.6.tgz", - "integrity": "sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.2.tgz", + "integrity": "sha512-P7oW7tLbYnhOLQk/Gv7cZgzgMPP/XN03K02/Jy6Y/NHzyIAIpxuZIM/YqAkfiXFPxA2CTm7NtCijK9EDu09u2w==", + "dev": true, "funding": [ { "type": "github", @@ -4081,9 +4027,11 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "strnum": "^2.1.2" + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.1.5", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" @@ -4093,8 +4041,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "pend": "~1.2.0" } @@ -4121,6 +4069,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "dev": true, "funding": [ { "type": "github", @@ -4132,7 +4081,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -4158,8 +4106,8 @@ "version": "21.3.0", "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", @@ -4211,42 +4159,12 @@ "dev": true, "license": "ISC" }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "peer": true, - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fetch-blob": "^3.1.2" }, @@ -4270,16 +4188,15 @@ } }, "node_modules/gaxios": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", - "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.2", - "rimraf": "^5.0.1" + "node-fetch": "^3.3.2" }, "engines": { "node": ">=18" @@ -4289,8 +4206,8 @@ "version": "8.1.2", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", @@ -4304,8 +4221,8 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -4315,7 +4232,6 @@ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -4327,8 +4243,8 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "pump": "^3.0.0" }, @@ -4356,8 +4272,8 @@ "version": "6.0.5", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", @@ -4371,8 +4287,8 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 14" } @@ -4381,8 +4297,8 @@ "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", @@ -4422,15 +4338,15 @@ } }, "node_modules/google-auth-library": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.1.tgz", - "integrity": "sha512-5awwuLrzNol+pFDmKJd0dKtZ0fPLAtoA5p7YO4ODsDu6ONJUVqbYwvv8y2ZBO5MBNp9TJXigB19710kYpBPdtA==", + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", + "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "7.1.3", + "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" @@ -4443,8 +4359,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=14" } @@ -4453,13 +4369,14 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC", - "peer": true + "dev": true, + "license": "ISC" }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4469,8 +4386,8 @@ "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": "*" } @@ -4479,8 +4396,8 @@ "version": "9.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "lru-cache": "^11.1.0" }, @@ -4492,8 +4409,8 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -4506,8 +4423,8 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -4520,6 +4437,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -4534,13 +4452,13 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -4574,11 +4492,11 @@ } }, "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 12" } @@ -4597,8 +4515,8 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -4620,24 +4538,9 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, "license": "ISC" }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "peer": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", @@ -4655,8 +4558,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "bignumber.js": "^9.0.0" } @@ -4672,8 +4575,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" @@ -4682,13 +4585,6 @@ "node": ">=16" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT", - "peer": true - }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -4700,8 +4596,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", @@ -4712,8 +4608,8 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" @@ -4735,7 +4631,7 @@ "integrity": "sha512-mnc0C0crx/xMSljb5s9QbnLrlFHprioFO1hkXyuSuO/QtbpLDa0l/uM21944UfQunMKmp3/r789DTDxVyyH6aA==", "hasInstallScript": true, "license": "MIT", - "peer": true, + "optional": true, "funding": { "url": "https://liberapay.com/Koromix" } @@ -4944,15 +4840,15 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "license": "Apache-2.0", - "peer": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/lru-cache": { "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", + "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "engines": { "node": "20 || >=22" } @@ -4962,7 +4858,6 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", - "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -4975,7 +4870,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -4985,7 +4879,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -5001,6 +4894,7 @@ "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" @@ -5016,8 +4910,8 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -5026,14 +4920,15 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -5048,11 +4943,11 @@ "license": "MIT" }, "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.1.1.tgz", + "integrity": "sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4.0" } @@ -5062,6 +4957,7 @@ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "deprecated": "Use your platform's native DOMException instead", + "dev": true, "funding": [ { "type": "github", @@ -5073,7 +4969,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=10.5.0" } @@ -5082,8 +4977,8 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -5101,8 +4996,8 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5111,18 +5006,18 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "wrappy": "1" } }, "node_modules/openai": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.10.0.tgz", - "integrity": "sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==", + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", + "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "openai": "bin/cli" }, @@ -5193,8 +5088,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" @@ -5207,8 +5102,8 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", @@ -5227,8 +5122,8 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" @@ -5237,13 +5132,6 @@ "node": ">= 14" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0", - "peer": true - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5261,15 +5149,15 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "parse5": "^6.0.1" } @@ -5278,15 +5166,15 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/partial-json": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", @@ -5298,10 +5186,27 @@ "node": ">=8" } }, + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5311,8 +5216,8 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" @@ -5328,8 +5233,8 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/picomatch": { "version": "4.0.4", @@ -5337,6 +5242,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5358,8 +5264,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", @@ -5370,30 +5276,30 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 4" } }, "node_modules/protobufjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", - "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.6.tgz", + "integrity": "sha512-M71sTMB146U3u0di3yup8iM+zv8yPRNQVr1KK4tyBitl3qFvEGucq/rGDRShD2rsJhtN02RJaJ7j5X5hmy8SJg==", + "dev": true, "hasInstallScript": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", + "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", + "@protobufjs/inquire": "^1.1.1", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.0.0" }, @@ -5405,8 +5311,8 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -5425,8 +5331,8 @@ "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -5435,15 +5341,15 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/pump": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -5463,18 +5369,8 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5503,94 +5399,17 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 4" } }, - "node_modules/rimraf": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", - "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", - "license": "ISC", - "peer": true, - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "peer": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC", - "peer": true - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.8.tgz", - "integrity": "sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw==", - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "peer": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, "funding": [ { "type": "github", @@ -5605,8 +5424,7 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/semver": { "version": "7.7.4", @@ -5625,6 +5443,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -5637,6 +5456,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5646,28 +5466,28 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC", - "peer": true + "dev": true, + "license": "ISC" }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.8.tgz", + "integrity": "sha512-NlGELfPrgX2f1TAAcz0WawlLn+0r3FyhhCRpFFK2CemXenPYvzMWWZINv3eDNo9ucdwme7oCHRY0Jnbs4aIkog==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "ip-address": "^10.0.1", + "ip-address": "^10.1.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -5679,8 +5499,8 @@ "version": "8.0.5", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", @@ -5694,9 +5514,9 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", "optional": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5705,31 +5525,15 @@ "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5743,22 +5547,8 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -5780,24 +5570,24 @@ } }, "node_modules/strnum": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", - "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.3.tgz", + "integrity": "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg==", + "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/strtok3": { "version": "10.3.4", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@tokenizer/token": "^0.3.0" }, @@ -5813,6 +5603,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -5825,8 +5616,8 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "any-promise": "^1.0.0" } @@ -5835,8 +5626,8 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -5865,8 +5656,8 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", @@ -5884,8 +5675,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/ts-api-utils": { "version": "2.5.0", @@ -5904,8 +5695,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "dev": true, + "license": "0BSD" }, "node_modules/tsx": { "version": "4.21.0", @@ -5940,12 +5731,20 @@ "node": ">= 0.8.0" } }, + "node_modules/typebox": { + "version": "1.1.36", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.36.tgz", + "integrity": "sha512-aGbdGqjFiCYy7XAe4+KUz53N2fB2ngumD1nImL2n6Hv0Tf8+bqx8k1DP+dOu3mLg3hOHbdl9K+2i7jomfYDk9A==", + "dev": true, + "license": "MIT" + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5982,8 +5781,8 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -5992,11 +5791,11 @@ } }, "node_modules/undici": { - "version": "7.22.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", - "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=20.18.1" } @@ -6005,6 +5804,7 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, "license": "MIT" }, "node_modules/uri-js": { @@ -6017,12 +5817,26 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 8" } @@ -6031,6 +5845,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -6056,27 +5871,8 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "license": "MIT", - "peer": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6093,15 +5889,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC", - "peer": true + "dev": true, + "license": "ISC" }, "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -6122,8 +5918,8 @@ "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -6132,8 +5928,8 @@ "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -6148,8 +5944,8 @@ "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", @@ -6167,8 +5963,8 @@ "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -6177,8 +5973,8 @@ "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" @@ -6201,8 +5997,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -6211,9 +6007,10 @@ } }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.1.tgz", + "integrity": "sha512-a6ENMBBGZBsnlSebQ/eKCguSBeGKSf4O7BPnqVPmYGtpBYI7VSqoVqw+QcB7kPRjbqPwhYTpFbVj/RqNz/CT0Q==", + "dev": true, "license": "MIT", "peer": true, "funding": { @@ -6221,13 +6018,13 @@ } }, "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", - "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, "license": "ISC", - "peer": true, "peerDependencies": { - "zod": "^3.25 || ^4" + "zod": "^3.25.28 || ^4" } } } diff --git a/package.json b/package.json index c2223632..3d43cb68 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "devDependencies": { "@biomejs/biome": "2.4.8", "@eslint/js": "^9.25.1", + "@mariozechner/pi-coding-agent": "^0.71.0", "@types/node": "^24.7.2", "eslint": "^9.25.1", "lefthook": "^2.1.4", diff --git a/script/image-attachments-e2e.ts b/script/image-attachments-e2e.ts new file mode 100644 index 00000000..1fcf6394 --- /dev/null +++ b/script/image-attachments-e2e.ts @@ -0,0 +1,527 @@ +import { execFileSync } from "node:child_process"; +import { existsSync, readFileSync } from "node:fs"; +import { mkdtemp, writeFile } from "node:fs/promises"; +import { createRequire } from "node:module"; +import { dirname, join, resolve } from "node:path"; +import { tmpdir } from "node:os"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +import installPiVim from "../index.js"; +import { + createExtensionApiHarness, + stubKeybindings, + stubTheme, + stubTui, +} from "../test/harness.js"; + +type RuntimeEditorFactory = ( + tui: typeof stubTui, + theme: typeof stubTheme, + keybindings: typeof stubKeybindings, +) => unknown; + +type WidgetCall = { + key: string; + content: string[] | undefined; + options: { placement?: string } | undefined; +}; + +type NotificationCall = { + message: string; + type: string; +}; + +type SentUserMessage = { + content: unknown; + options: unknown; +}; + +type RuntimeContext = { + cwd: string; + hasUI: boolean; + isIdle(): boolean; + ui: { + theme: typeof stubTheme; + setWidget(key: string, content: string[] | undefined, options?: { placement?: string }): void; + setEditorComponent(factory: RuntimeEditorFactory | undefined): void; + getEditorComponent(): RuntimeEditorFactory | undefined; + notify(message: string, type: string): void; + }; + shutdown(): void; +}; + +type RuntimeHarness = { + ctx: RuntimeContext; + widgetCalls: WidgetCall[]; + notifications: NotificationCall[]; + getEditorFactory(): RuntimeEditorFactory; +}; + +type EditorSurface = { + render(width: number): string[]; + invalidate(): void; + handleInput(data: string): void; + getText(): string; + setText(text: string): void; + addToHistory(text: string): void; + insertTextAtCursor(text: string): void; + getExpandedText(): string; + setAutocompleteProvider(provider: unknown): void; + setPaddingX(padding: number): void; + setAutocompleteMaxVisible(maxVisible: number): void; + onAction(action: string): void; + getLines(): string[]; + getCursor(): { line: number; col: number }; + getMode(): string; +}; + +type PiExtension = (pi: unknown) => void; + +const IMAGE_PACKAGE_NAME = "@jordyvd/pi-image-attachments"; +const BRACKETED_PASTE_START = "\x1b[200~"; +const BRACKETED_PASTE_END = "\x1b[201~"; +const PNG_BYTES = Buffer.from( + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+/p9sAAAAASUVORK5CYII=", + "base64", +); +const WRAPPABLE_BASELINE_METHODS = [ + "render", + "invalidate", + "handleInput", + "getText", + "setText", + "addToHistory", + "insertTextAtCursor", + "getExpandedText", + "setAutocompleteProvider", + "setPaddingX", + "setAutocompleteMaxVisible", + "onAction", + "getLines", + "getCursor", + "getMode", +] as const; +const WRAPPABLE_BASELINE_FIELDS = [ + "onSubmit", + "onChange", + "borderColor", + "focused", + "disableSubmit", + "actionHandlers", + "onEscape", + "onCtrlD", + "onPasteImage", + "onExtensionShortcut", +] as const; + +const currentRequire = createRequire(import.meta.url); +const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} + +function formatUnknownError(error: unknown): string { + if (error instanceof Error) return error.message; + return String(error); +} + +function readPackageName(packageJsonPath: string): string | null { + let parsed: unknown; + try { + parsed = JSON.parse(readFileSync(packageJsonPath, "utf8")) as unknown; + } catch (error) { + throw new Error( + `FAIL-INFRA: unable to read or parse package.json at ${packageJsonPath}: ${formatUnknownError(error)}`, + { cause: error }, + ); + } + + if (isRecord(parsed) && typeof parsed.name === "string") return parsed.name; + return null; +} + +function findPackageRoot(specifier: string): string { + const nodeModulesCandidate = join(projectRoot, "node_modules", ...specifier.split("/")); + if (hasPackageName(nodeModulesCandidate, specifier)) { + return nodeModulesCandidate; + } + + let dir: string; + try { + dir = dirname(currentRequire.resolve(specifier)); + } catch (error) { + if (isRecord(error) && error.code === "MODULE_NOT_FOUND") { + throw new Error(`FAIL-INFRA: unable to locate installed package root for ${specifier}`); + } + throw new Error( + `FAIL-INFRA: unable to resolve installed package root for ${specifier}: ${formatUnknownError(error)}`, + { cause: error }, + ); + } + + while (true) { + const packageJsonPath = join(dir, "package.json"); + if (existsSync(packageJsonPath) && readPackageName(packageJsonPath) === specifier) { + return dir; + } + + const parent = dirname(dir); + if (parent === dir) break; + dir = parent; + } + + throw new Error(`FAIL-INFRA: unable to locate installed package root for ${specifier}`); +} + +function hasPackageName(packageDir: string, expectedName: string): boolean { + const packageJsonPath = join(packageDir, "package.json"); + return existsSync(packageJsonPath) && readPackageName(packageJsonPath) === expectedName; +} + +function packLocalImageAttachments(packageDir: string, workspace: string): string { + try { + const output = execFileSync("npm", ["pack", packageDir, "--pack-destination", workspace], { + cwd: workspace, + encoding: "utf8", + env: { + ...process.env, + npm_config_ignore_scripts: "true", + }, + stdio: ["ignore", "pipe", "pipe"], + }).trim(); + const tarballName = output.split("\n").filter(Boolean).at(-1); + if (!tarballName) throw new Error("npm pack did not report a tarball name"); + return `file:${join(workspace, tarballName)}`; + } catch (error) { + throw new Error(`FAIL-INFRA: unable to pack ${IMAGE_PACKAGE_NAME}: ${formatUnknownError(error)}`); + } +} + +function resolveImageAttachmentsDependency(workspace: string): string { + const explicitCandidate = process.env.PI_IMAGE_ATTACHMENTS_PACKAGE_DIR; + if (explicitCandidate) { + if (!hasPackageName(explicitCandidate, IMAGE_PACKAGE_NAME)) { + throw new Error( + `FAIL-INFRA: PI_IMAGE_ATTACHMENTS_PACKAGE_DIR does not point to ${IMAGE_PACKAGE_NAME}: ${explicitCandidate}`, + ); + } + return packLocalImageAttachments(explicitCandidate, workspace); + } + + const candidates = [ + resolve(process.cwd(), "../pi-image-attachments"), + resolve(process.cwd(), "../../../pi-image-attachments"), + ]; + + for (const candidate of candidates) { + if (hasPackageName(candidate, IMAGE_PACKAGE_NAME)) { + return packLocalImageAttachments(candidate, workspace); + } + } + + return "^0.1.1"; +} + +function runNpmInstall(workspace: string): void { + try { + execFileSync("npm", ["install", "--ignore-scripts"], { + cwd: workspace, + encoding: "utf8", + env: { + ...process.env, + npm_config_audit: "false", + npm_config_fund: "false", + }, + stdio: ["ignore", "pipe", "pipe"], + }); + } catch (error) { + const output = isRecord(error) + ? [error.stdout, error.stderr].filter((value): value is string => typeof value === "string").join("\n") + : ""; + throw new Error( + `FAIL-INFRA: npm install --ignore-scripts failed${output ? `\n${output}` : ""}`, + ); + } +} + +async function createWorkspace(): Promise { + const workspace = await mkdtemp(join(tmpdir(), "pi-vim-image-attachments-e2e-")); + const packageJson = { + private: true, + type: "module", + dependencies: { + [IMAGE_PACKAGE_NAME]: resolveImageAttachmentsDependency(workspace), + "@mariozechner/pi-ai": `file:${findPackageRoot("@mariozechner/pi-ai")}`, + "@mariozechner/pi-coding-agent": `file:${findPackageRoot("@mariozechner/pi-coding-agent")}`, + "@mariozechner/pi-tui": `file:${findPackageRoot("@mariozechner/pi-tui")}`, + }, + }; + + await writeFile(join(workspace, "package.json"), `${JSON.stringify(packageJson, null, 2)}\n`); + runNpmInstall(workspace); + await writeFile(join(workspace, "fixture.png"), PNG_BYTES); + return workspace; +} + +async function importImageAttachmentsExtension(workspace: string): Promise { + try { + const workspaceRequire = createRequire(join(workspace, "package.json")); + const entry = workspaceRequire.resolve(`${IMAGE_PACKAGE_NAME}/index.ts`); + const module = await import(pathToFileURL(entry).href) as unknown; + + if (!isRecord(module) || typeof module.default !== "function") { + throw new Error(`${IMAGE_PACKAGE_NAME} default export is not a function`); + } + + return module.default as PiExtension; + } catch (error) { + throw new Error(`FAIL-INFRA: unable to import ${IMAGE_PACKAGE_NAME}: ${formatUnknownError(error)}`); + } +} + +function createPiHarness() { + const sentUserMessages: SentUserMessage[] = []; + const pi = Object.assign(createExtensionApiHarness(), { + sentUserMessages, + sendUserMessage(content: unknown, options?: unknown): void { + sentUserMessages.push({ content, options }); + }, + }); + + return pi; +} + +function createRuntimeHarness(cwd: string): RuntimeHarness { + let editorFactory: RuntimeEditorFactory | undefined; + const widgetCalls: WidgetCall[] = []; + const notifications: NotificationCall[] = []; + + const ctx: RuntimeContext = { + cwd, + hasUI: true, + isIdle() { + return true; + }, + ui: { + theme: stubTheme, + setWidget(key: string, content: string[] | undefined, options?: { placement?: string }): void { + widgetCalls.push({ key, content, options }); + }, + setEditorComponent(factory: RuntimeEditorFactory | undefined): void { + editorFactory = factory; + }, + getEditorComponent(): RuntimeEditorFactory | undefined { + return editorFactory; + }, + notify(message: string, type: string): void { + notifications.push({ message, type }); + }, + }, + shutdown(): void {}, + }; + + return { + ctx, + widgetCalls, + notifications, + getEditorFactory(): RuntimeEditorFactory { + if (!editorFactory) throw new Error("expected an installed editor factory"); + return editorFactory; + }, + }; +} + +async function installInOrder( + workspace: string, + imageExtension: PiExtension, + order: "image-first" | "vim-first", +): Promise { + const pi = createPiHarness(); + const harness = createRuntimeHarness(workspace); + + if (order === "image-first") { + imageExtension(pi); + installPiVim(pi); + } else { + installPiVim(pi); + imageExtension(pi); + } + + await pi.emit("session_start", undefined, harness.ctx); + return harness; +} + +function mountEditor(harness: RuntimeHarness): EditorSurface { + const editor = harness.getEditorFactory()(stubTui, stubTheme, stubKeybindings); + assertEditorSurface(editor, "installed editor"); + return editor; +} + +function fail(message: string): never { + throw new Error(message); +} + +function crossPackageBlocker(message: string): never { + fail(`cross-package blocker: ${message}`); +} + +function assertEditorSurface(editor: unknown, label: string): asserts editor is EditorSurface { + if (!isRecord(editor)) fail(`${label} is not an object`); + + for (const method of WRAPPABLE_BASELINE_METHODS) { + if (typeof editor[method] !== "function") { + fail(`${label} is missing method ${method}`); + } + } + + for (const field of WRAPPABLE_BASELINE_FIELDS) { + if (!(field in editor)) { + fail(`${label} is missing field ${field}`); + } + } + + if (!(editor.actionHandlers instanceof Map)) fail(`${label} actionHandlers is not a Map`); + if (typeof editor.borderColor !== "function") fail(`${label} borderColor is not a function`); + if (typeof editor.focused !== "boolean") fail(`${label} focused is not a boolean`); + if (typeof editor.disableSubmit !== "boolean") fail(`${label} disableSubmit is not a boolean`); +} + +function assertPiVimSurfaceForLaterDecorator(editor: unknown): asserts editor is EditorSurface { + try { + assertEditorSurface(editor, "later image-attachments editor"); + } catch (error) { + crossPackageBlocker(formatUnknownError(error)); + } +} + +function assertEqual(actual: unknown, expected: unknown, message: string): void { + if (actual !== expected) { + fail(`${message}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`); + } +} + +function assertIncludes(haystack: string, needle: string, message: string): void { + if (!haystack.includes(needle)) { + fail(`${message}: expected ${JSON.stringify(haystack)} to include ${JSON.stringify(needle)}`); + } +} + +function bracketedPaste(text: string): string { + return `${BRACKETED_PASTE_START}${text}${BRACKETED_PASTE_END}`; +} + +function typeText(editor: EditorSurface, text: string): void { + for (const char of text) editor.handleInput(char); +} + +function assertImageAttachmentState( + editor: EditorSurface, + harness: RuntimeHarness, + messagePrefix: string, +): void { + assertEqual( + editor.getText(), + "[Image #1] ", + `${messagePrefix} should insert an attachment placeholder`, + ); + + const latestWidget = harness.widgetCalls.at(-1); + if (!latestWidget?.content) fail(`${messagePrefix} should publish an attachments widget`); + assertIncludes( + latestWidget.content.join("\n"), + "[Image #1]", + `${messagePrefix} attachments widget should include the placeholder`, + ); +} + +function assertImageAttachmentInsertedByBracketedPaste( + editor: EditorSurface, + harness: RuntimeHarness, + imagePath: string, +): void { + editor.handleInput(bracketedPaste(imagePath)); + assertImageAttachmentState(editor, harness, "bracketed image paste"); +} + +function assertImageAttachmentInsertedByDirectInsert( + editor: EditorSurface, + harness: RuntimeHarness, + imagePath: string, +): void { + editor.insertTextAtCursor(imagePath); + assertImageAttachmentState(editor, harness, "direct image path insert"); +} + +function assertPiVimModalBehavior(editor: EditorSurface): void { + assertEqual(editor.getMode(), "insert", "editor should start in INSERT mode"); + typeText(editor, "abc"); + assertEqual(editor.getText(), "abc", "INSERT input should update editor text"); + + editor.handleInput("\x1b"); + assertEqual(editor.getMode(), "normal", "escape should enter NORMAL mode"); + + editor.handleInput("0"); + editor.handleInput("x"); + assertEqual( + editor.getText(), + "bc", + "NORMAL printable input should be handled by pi-vim instead of inserted as raw text", + ); +} + +async function verifyImageThenVim(workspace: string, imageExtension: PiExtension): Promise { + const imagePath = join(workspace, "fixture.png"); + + const imageHarness = await installInOrder(workspace, imageExtension, "image-first"); + assertImageAttachmentInsertedByBracketedPaste(mountEditor(imageHarness), imageHarness, imagePath); + + const directImageHarness = await installInOrder(workspace, imageExtension, "image-first"); + assertImageAttachmentInsertedByDirectInsert(mountEditor(directImageHarness), directImageHarness, imagePath); + + const modalHarness = await installInOrder(workspace, imageExtension, "image-first"); + assertPiVimModalBehavior(mountEditor(modalHarness)); + + console.log("PASS image-attachments then pi-vim"); +} + +async function verifyVimThenImage(workspace: string, imageExtension: PiExtension): Promise { + const imagePath = join(workspace, "fixture.png"); + + try { + const surfaceHarness = await installInOrder(workspace, imageExtension, "vim-first"); + assertPiVimSurfaceForLaterDecorator(surfaceHarness.getEditorFactory()(stubTui, stubTheme, stubKeybindings)); + + const imageHarness = await installInOrder(workspace, imageExtension, "vim-first"); + const imageEditor = mountEditor(imageHarness); + assertImageAttachmentInsertedByBracketedPaste(imageEditor, imageHarness, imagePath); + assertEqual(imageEditor.getMode(), "insert", "image attachment handling should preserve INSERT mode"); + + const directImageHarness = await installInOrder(workspace, imageExtension, "vim-first"); + const directImageEditor = mountEditor(directImageHarness); + assertImageAttachmentInsertedByDirectInsert(directImageEditor, directImageHarness, imagePath); + assertEqual(directImageEditor.getMode(), "insert", "direct image path insert should preserve INSERT mode"); + + const modalHarness = await installInOrder(workspace, imageExtension, "vim-first"); + assertPiVimModalBehavior(mountEditor(modalHarness)); + } catch (error) { + const message = formatUnknownError(error); + if (message.startsWith("cross-package blocker:")) throw error; + crossPackageBlocker(message); + } + + console.log("PASS pi-vim then image-attachments"); +} + +async function main(): Promise { + const workspace = await createWorkspace(); + console.log("image-attachments-e2e: npm install --ignore-scripts completed"); + + const imageExtension = await importImageAttachmentsExtension(workspace); + await verifyImageThenVim(workspace, imageExtension); + await verifyVimThenImage(workspace, imageExtension); + + console.log("PASS image-attachments-e2e"); +} + +void main(); diff --git a/script/pack-check.ts b/script/pack-check.ts index dd962e09..a1ba3a48 100644 --- a/script/pack-check.ts +++ b/script/pack-check.ts @@ -62,9 +62,12 @@ const FORBIDDEN_REGEX_BY_GLOB: Record<(typeof FORBIDDEN_GLOBS)[number], RegExp> const THRESHOLDS = { maxFiles: 12, // WORD/delimited text objects add a packaged resolver module plus README surface. - // Keep budgets tight enough to catch accidental docs/tests in the package. - maxSize: 31000, - maxUnpackedSize: 136000, + // Composable editor factory (#3935) adds the createModalEditor mixin wrapper, + // delegation model docs, and EX paste provenance for pasted CSI bytes. + // Review hardening keeps non-obvious WHY comments and expands delegation + // helpers for maintainability. Keep budgets tight enough to catch accidents. + maxSize: 35600, + maxUnpackedSize: 157000, } as const; function compareStrings(a: string, b: string): number { diff --git a/test/harness.ts b/test/harness.ts index e13a75a9..1858f565 100644 --- a/test/harness.ts +++ b/test/harness.ts @@ -2,7 +2,10 @@ * Test harness for ModalEditor integration tests. */ -import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"; +import { + CustomEditor, + type ExtensionAPI, +} from "@mariozechner/pi-coding-agent"; import { ModalEditor } from "../index.js"; @@ -120,6 +123,145 @@ export const stubKeybindings = { matches: () => false, } as unknown as ModalEditorConstructorArgs[2]; +export class CompatibleDelegateEditor extends CustomEditor { + readonly rawInputs: string[] = []; + readonly invalidations: number[] = []; + + renderLines: string[] | null = null; + + override handleInput(data: string): void { + this.rawInputs.push(data); + super.handleInput(data); + } + + override invalidate(): void { + this.invalidations.push(this.invalidations.length + 1); + super.invalidate(); + } + + override render(width: number): string[] { + if (this.renderLines) return [...this.renderLines]; + return super.render(width); + } +} + +export type DelegateSyncSnapshot = { + onSubmit?: unknown; + onChange?: unknown; + onEscape?: unknown; + onCtrlD?: unknown; + onPasteImage?: unknown; + onExtensionShortcut?: unknown; + actionHandlers?: unknown; + borderColor?: unknown; + focused?: boolean; + disableSubmit?: boolean; +}; + +export class StockSurfaceDelegateEditor extends CompatibleDelegateEditor { + readonly expandedTextCalls: number[] = []; + readonly historyAdds: string[] = []; + readonly autocompleteProviders: Parameters[0][] = []; + readonly paddingXValues: number[] = []; + readonly autocompleteMaxVisibleValues: number[] = []; + readonly insertedTexts: string[] = []; + readonly setTextCalls: string[] = []; + readonly inputSyncSnapshots: DelegateSyncSnapshot[] = []; + + expandedTextResult = "expanded delegate text"; + + override handleInput(data: string): void { + this.inputSyncSnapshots.push({ + onSubmit: this.onSubmit, + onChange: this.onChange, + onEscape: this.onEscape, + onCtrlD: this.onCtrlD, + onPasteImage: this.onPasteImage, + onExtensionShortcut: this.onExtensionShortcut, + actionHandlers: this.actionHandlers, + borderColor: this.borderColor, + focused: this.focused, + disableSubmit: this.disableSubmit, + }); + super.handleInput(data); + } + + override getExpandedText(): string { + this.expandedTextCalls.push(this.expandedTextCalls.length + 1); + return this.expandedTextResult; + } + + override addToHistory(text: string): void { + this.historyAdds.push(text); + super.addToHistory(text); + } + + override setAutocompleteProvider( + provider: Parameters[0], + ): void { + this.autocompleteProviders.push(provider); + super.setAutocompleteProvider(provider); + } + + override setPaddingX(padding: number): void { + this.paddingXValues.push(padding); + super.setPaddingX(padding); + } + + override setAutocompleteMaxVisible(maxVisible: number): void { + this.autocompleteMaxVisibleValues.push(maxVisible); + super.setAutocompleteMaxVisible(maxVisible); + } + + override insertTextAtCursor(text: string): void { + this.insertedTexts.push(text); + super.insertTextAtCursor(text); + } + + override setText(text: string): void { + this.setTextCalls.push(text); + super.setText(text); + } +} + +export function createStockSurfaceDelegateEditor(): StockSurfaceDelegateEditor { + return new StockSurfaceDelegateEditor(stubTui, stubTheme, stubKeybindings); +} + +export function createCompatibleDelegateEditor( + renderLines?: string[], +): CompatibleDelegateEditor { + const editor = new CompatibleDelegateEditor(stubTui, stubTheme, stubKeybindings); + editor.renderLines = renderLines ?? null; + return editor; +} + +export function createMinimalIncompatibleEditor(): { + render(width: number): string[]; + invalidate(): void; + handleInput(data: string): void; + getText(): string; + setText(text: string): void; +} { + let text = ""; + return { + render(width: number): string[] { + void width; + return [text]; + }, + invalidate(): void {}, + handleInput(data: string): void { + text += data; + }, + getText(): string { + return text; + }, + setText(nextText: string): void { + text = nextText; + }, + }; +} + /** * Send an array of key events to the editor. * Each element is one atomic key press (may be a multi-byte escape sequence). diff --git a/test/modal-editor.test.ts b/test/modal-editor.test.ts index d9dcfb82..061726cf 100644 --- a/test/modal-editor.test.ts +++ b/test/modal-editor.test.ts @@ -10,16 +10,22 @@ import { spawn } from "node:child_process"; import { readFile } from "node:fs/promises"; import { describe, it } from "node:test"; import assert from "node:assert/strict"; +import { CustomEditor } from "@mariozechner/pi-coding-agent"; import { CURSOR_MARKER, visibleWidth } from "@mariozechner/pi-tui"; import type { WordMotionClass } from "../motions.js"; import type { WordMotionDirection, WordMotionTarget } from "../word-boundary-cache.js"; -import installPiVim, { ModalEditor } from "../index.js"; +import installPiVim, { createModalEditor, ModalEditor } from "../index.js"; import { setPiVimSettingsReaderForTests } from "../clipboard-policy.js"; import { + CompatibleDelegateEditor, + type DelegateSyncSnapshot, + createCompatibleDelegateEditor, createCursorShapeTui, createEditorWithSpy, createExtensionApiHarness, + createMinimalIncompatibleEditor, createMultiLineEditor, + createStockSurfaceDelegateEditor, sendKeys, stubKeybindings, stubTheme, @@ -68,6 +74,30 @@ type EditorFactory = ( keybindings: ConstructorParameters[2], ) => ModalEditor; +type PreviousEditorFactory = ( + tui: ConstructorParameters[0], + theme: ConstructorParameters[1], + keybindings: ConstructorParameters[2], +) => unknown; + +type ModalEditorFourthArg = ConstructorParameters[3]; +type BaseEditorFourthArg = ConstructorParameters[3]; + +const modalEditorBaseOptionsArg: BaseEditorFourthArg = { paddingX: 1, autocompleteMaxVisible: 2 }; +const modalEditorFourthArgFixtures = [ + modalEditorBaseOptionsArg satisfies ModalEditorFourthArg, + null satisfies ModalEditorFourthArg, + { + insert: (s: string) => s, + normal: (s: string) => s, + ex: (s: string) => s, + } satisfies ModalEditorFourthArg, +]; +// @ts-expect-error ModalEditor constructor arg 4 must not widen to unknown. +const modalEditorFourthArgRejectsNumber = 1 satisfies ModalEditorFourthArg; +void modalEditorFourthArgFixtures; +void modalEditorFourthArgRejectsNumber; + type NotificationCall = { message: string; type: string }; function getRawEditor(editor: ModalEditor): ModalEditorTestInternals { @@ -87,6 +117,10 @@ function focusEditor(editor: ModalEditor): void { editor.focused = true; } +function setInsertDelegateForTest(editor: ModalEditor, delegate: CompatibleDelegateEditor): void { + editor.setInsertDelegate(delegate as unknown as Parameters[0]); +} + function findCursorMarkerLine(lines: string[]): string { const line = lines.find((line) => line.includes(CURSOR_MARKER)); assert.ok(line, "expected rendered lines to include CURSOR_MARKER"); @@ -124,6 +158,8 @@ function setInternalCursor(editor: ModalEditor, cursorCol: number, cursorLine: n type InstalledExtension = { editorFactory: EditorFactory; + readonly getEditorComponentCalls: number; + readonly uiCallOrder: string[]; readonly notificationCalls: number; readonly notifications: NotificationCall[]; readonly shutdownCalls: number; @@ -132,9 +168,13 @@ type InstalledExtension = { readonly sessionEndHandlerCount: number; }; -async function installExtensionWithEditorFactory(): Promise { +async function installExtensionWithEditorFactory( + previousFactory?: PreviousEditorFactory, +): Promise { const pi = createExtensionApiHarness(); let editorFactory: EditorFactory | null = null; + let getEditorComponentCalls = 0; + const uiCallOrder: string[] = []; let notificationCalls = 0; const notifications: NotificationCall[] = []; let shutdownCalls = 0; @@ -144,8 +184,14 @@ async function installExtensionWithEditorFactory(): Promise ui: { theme: stubTheme, setEditorComponent(factory: EditorFactory): void { + uiCallOrder.push("setEditorComponent"); editorFactory = factory; }, + getEditorComponent(): PreviousEditorFactory | undefined { + uiCallOrder.push("getEditorComponent"); + getEditorComponentCalls++; + return previousFactory; + }, notify(message: string, type: string): void { notificationCalls++; notifications.push({ message, type }); @@ -165,6 +211,12 @@ async function installExtensionWithEditorFactory(): Promise return { editorFactory, + get getEditorComponentCalls() { + return getEditorComponentCalls; + }, + get uiCallOrder() { + return uiCallOrder; + }, get notificationCalls() { return notificationCalls; }, @@ -233,6 +285,188 @@ type HelperRunResult = { stderr: string; }; +const WRAPPABLE_BASELINE_METHODS = [ + "render", + "invalidate", + "handleInput", + "getText", + "setText", + "addToHistory", + "insertTextAtCursor", + "getExpandedText", + "setAutocompleteProvider", + "setPaddingX", + "setAutocompleteMaxVisible", + "onAction", + "getLines", + "getCursor", + "getMode", +] as const; + +const WRAPPABLE_BASELINE_FIELDS = [ + "onSubmit", + "onChange", + "borderColor", + "focused", + "disableSubmit", + "actionHandlers", + "onEscape", + "onCtrlD", + "onPasteImage", + "onExtensionShortcut", +] as const; + +function assertWrappableBaselineSurface(editor: ModalEditor): void { + const surface = editor as unknown as Record; + + for (const method of WRAPPABLE_BASELINE_METHODS) { + assert.equal(typeof surface[method], "function", `expected ${method} method`); + } + + for (const field of WRAPPABLE_BASELINE_FIELDS) { + assert.equal(field in surface, true, `expected ${field} field`); + } + + assert.equal(surface.actionHandlers instanceof Map, true, "expected actionHandlers Map"); + assert.equal(typeof surface.borderColor, "function", "expected borderColor function"); + assert.equal(typeof surface.focused, "boolean", "expected focused boolean"); + assert.equal(typeof surface.disableSubmit, "boolean", "expected disableSubmit boolean"); +} + +function createTransparentEditorWrapper( + editor: ModalEditor, + interceptedInputs: string[], +): ModalEditor { + const source = editor as unknown as Record; + + return new Proxy({} as Record, { + get(_target, property) { + if (property === "handleInput") { + return (data: string): void => { + interceptedInputs.push(data); + const handleInput = source.handleInput; + if (typeof handleInput !== "function") { + throw new TypeError("expected handleInput method"); + } + handleInput.call(editor, data); + }; + } + + const value = source[property]; + return typeof value === "function" ? value.bind(editor) : value; + }, + set(_target, property, value) { + source[property] = value; + return true; + }, + has(_target, property) { + return property in source; + }, + }) as unknown as ModalEditor; +} + +type ImageAttachmentWidgetCall = { + content: string[]; +}; + +const IMAGE_ATTACHMENT_CONTRACT_PATH = "/tmp/pi-vim-contract-fixture.png"; +const BRACKETED_PASTE_START = "\x1b[200~"; +const BRACKETED_PASTE_END = "\x1b[201~"; + +function bracketedPaste(text: string): string { + return `${BRACKETED_PASTE_START}${text}${BRACKETED_PASTE_END}`; +} + +function extractBracketedPastePayload(data: string): string | null { + if (!data.startsWith(BRACKETED_PASTE_START)) return null; + if (!data.endsWith(BRACKETED_PASTE_END)) return null; + + return data.slice( + BRACKETED_PASTE_START.length, + data.length - BRACKETED_PASTE_END.length, + ); +} + +function isContractImagePath(text: string): boolean { + return text === IMAGE_ATTACHMENT_CONTRACT_PATH; +} + +function recordImageAttachment( + widgetCalls: ImageAttachmentWidgetCall[], + imageIndex: number, +): string { + const placeholder = `[Image #${imageIndex}] `; + widgetCalls.push({ content: [placeholder.trim()] }); + return placeholder; +} + +class ImageAttachmentsStyleDelegateEditor extends CompatibleDelegateEditor { + readonly widgetCalls: ImageAttachmentWidgetCall[] = []; + private nextImageIndex = 1; + + override handleInput(data: string): void { + const pastedText = extractBracketedPastePayload(data); + if (pastedText !== null && isContractImagePath(pastedText)) { + this.insertTextAtCursor(pastedText); + return; + } + + super.handleInput(data); + } + + override insertTextAtCursor(text: string): void { + if (isContractImagePath(text)) { + const placeholder = recordImageAttachment(this.widgetCalls, this.nextImageIndex); + this.nextImageIndex++; + super.insertTextAtCursor(placeholder); + return; + } + + super.insertTextAtCursor(text); + } +} + +function createImageAttachmentsStyleDelegate(): ImageAttachmentsStyleDelegateEditor { + return new ImageAttachmentsStyleDelegateEditor(stubTui, stubTheme, stubKeybindings); +} + +function decorateImageAttachmentsStyleEditor(editor: ModalEditor): { + editor: ModalEditor; + widgetCalls: ImageAttachmentWidgetCall[]; +} { + const widgetCalls: ImageAttachmentWidgetCall[] = []; + const originalHandleInput = editor.handleInput.bind(editor); + const originalInsertTextAtCursor = editor.insertTextAtCursor.bind(editor); + let nextImageIndex = 1; + + const insertImagePlaceholder = (): void => { + const placeholder = recordImageAttachment(widgetCalls, nextImageIndex); + nextImageIndex++; + originalInsertTextAtCursor(placeholder); + }; + + editor.handleInput = (data: string): void => { + const pastedText = extractBracketedPastePayload(data); + if (pastedText !== null && isContractImagePath(pastedText)) { + insertImagePlaceholder(); + return; + } + + originalHandleInput(data); + }; + + editor.insertTextAtCursor = (text: string): void => { + if (isContractImagePath(text)) { + insertImagePlaceholder(); + return; + } + + originalInsertTextAtCursor(text); + }; + + return { editor, widgetCalls }; +} + const CLIPBOARD_HELPER_TEST_TIMEOUT_MS = 5_000; async function getClipboardHelperSourceWithMock(mockModuleSource: string): Promise { @@ -575,6 +809,25 @@ describe("mode transitions", () => { assert.equal(editor.getMode(), "normal"); }); + it("normal mode discards split paste payload that looks like key release", () => { + const { editor } = createEditorWithSpy("abc"); + + sendKeys(editor, ["\x1b[200~", "90:62:3F:A5\x1b[201~", "x"]); + + assert.equal(editor.getText(), "bc"); + assert.equal(editor.getRegister(), "a"); + assert.equal(editor.getMode(), "normal"); + }); + + it("normal mode filters key release remaining after split paste tail", () => { + const { editor } = createEditorWithSpy("abcd"); + + sendKeys(editor, ["x", "\x1b[200~", "\x1b[201~\x1b[95;5:3u"]); + + assert.equal(editor.getText(), "bcd"); + assert.equal(editor.getMode(), "normal"); + }); + it("insert mode keeps bracketed paste payload text", () => { const { editor } = createEditorWithSpy("abc"); sendKeys(editor, ["i", "\x1b[200~PASTE\x1b[201~"]); @@ -582,6 +835,24 @@ describe("mode transitions", () => { assert.equal(editor.getMode(), "insert"); }); + it("insert mode keeps split bracketed paste payload that looks like key release", () => { + const { editor } = createEditorWithSpy("abc"); + + sendKeys(editor, ["i", "\x1b[200~", "90:62:3F:A5\x1b[201~", "x"]); + + assert.equal(editor.getText(), "90:62:3F:A5xabc"); + assert.equal(editor.getMode(), "insert"); + }); + + it("insert mode keeps unwrapped text chunks that look like key release", () => { + const { editor } = createEditorWithSpy("abc"); + + sendKeys(editor, ["i", "90:62:3F:A5"]); + + assert.equal(editor.getText(), "90:62:3F:A5abc"); + assert.equal(editor.getMode(), "insert"); + }); + it("escape from insert clears unterminated bracketed paste state", () => { const { editor } = createEditorWithSpy("abc"); @@ -640,7 +911,8 @@ describe("ex mini-mode", () => { it("renders EX labels with the EX-specific colorizer", () => { const calls: string[] = []; - const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings, { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + editor.setColorizers({ insert: (s: string) => { calls.push(`insert:${s}`); return `\x1b[32m${s}\x1b[39m`; @@ -665,6 +937,60 @@ describe("ex mini-mode", () => { assert.ok(footer.endsWith("\x1b[35m EX :_ \x1b[39m")); }); + it("accepts legacy constructor colorizers for the INSERT label", () => { + const calls: string[] = []; + const colorizers = { + insert: (s: string) => { + calls.push(`insert:${s}`); + return `${s}`; + }, + normal: (s: string) => { + calls.push(`normal:${s}`); + return `${s}`; + }, + ex: (s: string) => { + calls.push(`ex:${s}`); + return `${s}`; + }, + }; + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings, colorizers); + + const footer = editor.render(80).at(-1) ?? ""; + + assert.deepEqual(calls, ["insert: INSERT "]); + assert.ok(footer.endsWith(" INSERT ")); + }); + + it("passes constructor editor options to the base editor", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings, { + paddingX: 3, + autocompleteMaxVisible: 7, + }); + + assert.equal(editor.getPaddingX(), 3); + assert.equal(editor.getAutocompleteMaxVisible(), 7); + }); + + it("does not classify base editor options as legacy colorizers", () => { + const receivedFourthArgs: unknown[] = []; + + class RecordingCustomEditor extends CustomEditor { + constructor(...args: ConstructorParameters) { + receivedFourthArgs.push(args[3]); + super(...args); + } + } + + const RecordingModalEditor = createModalEditor(RecordingCustomEditor); + const options = { paddingX: 3, autocompleteMaxVisible: 7 }; + + const editor = new RecordingModalEditor(stubTui, stubTheme, stubKeybindings, options); + + assert.deepEqual(receivedFourthArgs, [options]); + assert.equal(editor.getPaddingX(), 3); + assert.equal(editor.getAutocompleteMaxVisible(), 7); + }); + it(":q refuses to quit when prompt has non-whitespace text", () => { const session = createEditorWithSpy("hello"); @@ -787,6 +1113,55 @@ describe("ex mini-mode", () => { assert.deepEqual(session.notifications, []); }); + it("ex mini-mode accepts split paste payload that looks like key release", () => { + const session = createEditorWithSpy("hello"); + + sendKeys(session.editor, [":", "\x1b[200~", "label:3u\x1b[201~"]); + + assert.ok(session.editor.render(80).at(-1)?.endsWith(" EX :label:3u_ ")); + assert.equal(session.editor.getMode(), "normal"); + assert.equal(session.editor.getText(), "hello"); + assert.deepEqual(session.notifications, []); + }); + + it("ex mini-mode renders escaped pasted CSI text that looks like key release", () => { + const session = createEditorWithSpy("hello"); + + sendKeys(session.editor, [":", "\x1b[200~\x1b[100;1:3u\x1b[201~"]); + + const footer = session.editor.render(80).at(-1) ?? ""; + const label = footer.slice(footer.lastIndexOf(" EX ")); + assert.equal(label.includes("\x1b"), false); + assert.equal(footer.includes("\x1b[100;1:3u"), false); + assert.ok(footer.endsWith(" EX :^[[100;1:3u_ ")); + assert.equal(session.editor.getMode(), "normal"); + assert.equal(session.editor.getText(), "hello"); + assert.deepEqual(session.notifications, []); + }); + + it("ex mini-mode escapes pasted control bytes in unsupported command notifications", () => { + const session = createEditorWithSpy("hello"); + + sendKeys(session.editor, [":", "\x1b[200~\x1b[2J\x1b[201~", "\r"]); + + assert.equal(session.quitCalls, 0); + assert.equal(session.notifications.some((notification) => notification.includes("\x1b")), false); + assert.deepEqual(session.notifications, ["Unsupported ex command: :^[[2J"]); + assert.equal(session.editor.getMode(), "normal"); + assert.equal(session.editor.getText(), "hello"); + }); + + it("ex mini-mode filters key release remaining after split paste tail", () => { + const session = createEditorWithSpy("hello"); + + sendKeys(session.editor, [":", "\x1b[200~", "\x1b[201~\x1b[100;1:3u"]); + + assert.ok(session.editor.render(80).at(-1)?.endsWith(" EX :_ ")); + assert.equal(session.editor.getMode(), "normal"); + assert.equal(session.editor.getText(), "hello"); + assert.deepEqual(session.notifications, []); + }); + it("newline in bracketed paste submits the pending ex command", () => { const session = createEditorWithSpy("hello"); @@ -998,6 +1373,1005 @@ describe("cursor shape lifecycle", () => { }); }); +describe("ModalEditor insert delegate routing", () => { + it("forwards ordinary INSERT input to the delegate", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("a"); + + assert.deepEqual(delegate.rawInputs, ["a"]); + assert.equal(editor.getText(), "a"); + assert.deepEqual(editor.getLines(), ["a"]); + assert.deepEqual(editor.getCursor(), { line: 0, col: 1 }); + }); + + it("exposes delegate key-release opt-in and forwards INSERT release events", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: boolean; + }; + const releaseSequence = "\x1b[97;1:3u"; + delegate.wantsKeyRelease = true; + + setInsertDelegateForTest(editor, delegate); + editor.handleInput(releaseSequence); + + assert.equal((editor as unknown as { wantsKeyRelease?: boolean }).wantsKeyRelease, true); + assert.equal(delegate.wantsKeyRelease, true); + assert.deepEqual(delegate.rawInputs, [releaseSequence]); + }); + + it("allows decorators to set key-release opt-in without changing a delegate", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: boolean; + }; + delegate.wantsKeyRelease = false; + setInsertDelegateForTest(editor, delegate); + + editor.wantsKeyRelease = true; + + assert.equal(editor.wantsKeyRelease, true); + assert.equal(delegate.wantsKeyRelease, false); + }); + + it("does not decode INSERT release events when only a decorator opted in", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + editor.wantsKeyRelease = true; + + editor.handleInput("\x1b[97;1:3u"); + + assert.equal(editor.getText(), ""); + }); + + it("does not forward INSERT releases to a delegate that did not opt in", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: boolean; + }; + delegate.wantsKeyRelease = false; + setInsertDelegateForTest(editor, delegate); + editor.wantsKeyRelease = true; + + editor.handleInput("\x1b[97;1:3u"); + + assert.equal(editor.getText(), ""); + assert.deepEqual(delegate.rawInputs, []); + }); + + it("does not let decorator key-release opt-in cancel pending NORMAL operators", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + editor.wantsKeyRelease = true; + + for (const char of "hello world") { + editor.handleInput(char); + } + sendKeys(editor, ["\x1b", "0", "d", "\x1b[100;1:3u", "w"]); + + assert.equal(editor.getText(), "world"); + assert.equal(editor.getRegister(), "hello "); + }); + + it("does not let decorator key-release opt-in repeat NORMAL undo or redo", () => { + const { editor } = createEditorWithSpy("abcd"); + editor.wantsKeyRelease = true; + + sendKeys(editor, ["x", "\x1b[95;5u", "\x1b[95;5:3u"]); + assert.equal(editor.getText(), "abcd"); + + sendKeys(editor, ["\x1b[114;5u", "\x1b[114;5:3u"]); + assert.equal(editor.getText(), "bcd"); + }); + + it("does not expose non-boolean delegate key-release values", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: unknown; + }; + delegate.wantsKeyRelease = "true"; + + setInsertDelegateForTest(editor, delegate); + + assert.equal(editor.wantsKeyRelease, undefined); + }); + + it("does not let delegate key-release opt-in cancel pending NORMAL operators", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: boolean; + }; + delegate.wantsKeyRelease = true; + setInsertDelegateForTest(editor, delegate); + + for (const char of "hello world") { + editor.handleInput(char); + } + sendKeys(editor, ["\x1b", "0", "d", "\x1b[100;1:3u", "w"]); + + assert.equal(editor.wantsKeyRelease, undefined); + assert.equal(editor.getText(), "world"); + assert.equal(editor.getRegister(), "hello "); + }); + + it("does not let delegate key-release opt-in cancel or pollute EX mini-mode", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: boolean; + }; + delegate.wantsKeyRelease = true; + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["\x1b", ":", "\x1b[100;1:3u"]); + + assert.equal(editor.wantsKeyRelease, undefined); + assert.ok(editor.render(80).at(-1)?.endsWith(" EX :_ ")); + assert.equal(editor.getText(), ""); + }); + + it("resyncs delegate key-release opt-in when delegate enables it during INSERT input", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: boolean; + }; + const originalHandleInput = delegate.handleInput.bind(delegate); + delegate.wantsKeyRelease = false; + delegate.handleInput = (data: string) => { + originalHandleInput(data); + delegate.wantsKeyRelease = true; + }; + + setInsertDelegateForTest(editor, delegate); + assert.equal((editor as unknown as { wantsKeyRelease?: boolean }).wantsKeyRelease, false); + + editor.handleInput("a"); + + assert.equal((editor as unknown as { wantsKeyRelease?: boolean }).wantsKeyRelease, true); + assert.deepEqual(delegate.rawInputs, ["a"]); + }); + + it("resyncs delegate key-release opt-in when delegate disables it during INSERT input", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor() as CompatibleDelegateEditor & { + wantsKeyRelease?: boolean; + }; + const originalHandleInput = delegate.handleInput.bind(delegate); + delegate.wantsKeyRelease = true; + delegate.handleInput = (data: string) => { + originalHandleInput(data); + delegate.wantsKeyRelease = false; + }; + + setInsertDelegateForTest(editor, delegate); + assert.equal((editor as unknown as { wantsKeyRelease?: boolean }).wantsKeyRelease, true); + + editor.handleInput("a"); + + assert.equal((editor as unknown as { wantsKeyRelease?: boolean }).wantsKeyRelease, false); + assert.deepEqual(delegate.rawInputs, ["a"]); + }); + + it("consumes escape in INSERT mode without forwarding it to the delegate", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("\x1b"); + + assert.deepEqual(delegate.rawInputs, []); + assert.equal(editor.getMode(), "normal"); + }); + + it("does not forward NORMAL printable input as raw delegate input", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("\x1b"); + editor.handleInput("x"); + + assert.deepEqual(delegate.rawInputs, []); + assert.equal(editor.getMode(), "normal"); + }); + + it("does not forward EX command text as raw delegate input", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["\x1b", ":", "q"]); + + assert.deepEqual(delegate.rawInputs, []); + assert.equal(editor.getMode(), "normal"); + }); + + it("does not forward NORMAL bracketed paste as raw delegate input", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["\x1b", "\x1b[200~PASTE\x1b[201~"]); + + assert.deepEqual(delegate.rawInputs, []); + assert.equal(editor.getMode(), "normal"); + }); + + it("routes NORMAL non-printable fallthrough through the delegate", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["a", "b", "c", "\x1b", "0", "\x1b[C", "x"]); + + assert.equal(editor.getText(), "ac"); + assert.equal(editor.getRegister(), "b"); + assert.deepEqual(delegate.rawInputs, ["a", "b", "c", "\x01", "\x1b[C"]); + }); + + it("routes non-printable operator cancellation through the delegate", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["a", "b", "c", "\x1b", "0", "d", "\x1b[C", "x"]); + + assert.equal(editor.getText(), "ac"); + assert.equal(editor.getRegister(), "b"); + assert.deepEqual(delegate.rawInputs, ["a", "b", "c", "\x01", "\x1b[C"]); + }); +}); + +describe("ModalEditor delegate-backed primitives", () => { + it("uses delegate cursor movement during INSERT for subsequent NORMAL x", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["a", "b", "c", "\x1b[D", "\x1b[D", "\x1b", "x"]); + + assert.deepEqual(delegate.rawInputs, ["a", "b", "c", "\x1b[D", "\x1b[D"]); + assert.equal(editor.getText(), "ac"); + assert.deepEqual(editor.getCursor(), { line: 0, col: 1 }); + }); + + it("applies open-line primitives to delegate-backed text", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["a", "b", "c", "\x1b", "o"]); + + assert.equal(editor.getMode(), "insert"); + assert.equal(editor.getText(), "abc\n"); + assert.deepEqual(editor.getCursor(), { line: 1, col: 0 }); + }); + + it("syncs delegate setText wrappers after NORMAL text mutations", () => { + class DraftTrackingDelegate extends CompatibleDelegateEditor { + draftPlaceholders = ["[Image #1]"]; + publishedDrafts: string[][] = []; + + override setText(text: string): void { + super.setText(text); + this.syncDraft(); + } + + private syncDraft(): void { + const text = this.getText(); + this.draftPlaceholders = this.draftPlaceholders.filter((placeholder) => text.includes(placeholder)); + this.publishedDrafts.push([...this.draftPlaceholders]); + } + } + + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = new DraftTrackingDelegate(stubTui, stubTheme, stubKeybindings); + delegate.setText("[Image #1] prompt"); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["\x1b", "d", "d"]); + + assert.equal(editor.getText(), ""); + assert.deepEqual(delegate.draftPlaceholders, []); + assert.deepEqual(delegate.publishedDrafts.at(-1), []); + }); + + it("uses delegate undo and pi-vim redo for delegate-backed edits", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + setInsertDelegateForTest(editor, delegate); + + sendKeys(editor, ["a", "b", "c", "\x1b", "0", "x"]); + assert.equal(editor.getText(), "bc"); + + sendKeys(editor, ["u"]); + assert.equal(editor.getText(), "abc"); + + sendKeys(editor, ["\x12"]); + assert.equal(editor.getText(), "bc"); + assert.deepEqual(editor.getCursor(), { line: 0, col: 0 }); + }); +}); + +describe("ModalEditor stock editor delegate surface", () => { + it("forwards stock editor methods to the delegate", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createStockSurfaceDelegateEditor(); + const provider: Parameters[0] = { + async getSuggestions() { + return null; + }, + applyCompletion(lines, cursorLine, cursorCol) { + return { lines, cursorLine, cursorCol }; + }, + }; + setInsertDelegateForTest(editor, delegate); + + editor.setText("prefill"); + editor.insertTextAtCursor(" plus"); + const expandedText = editor.getExpandedText(); + editor.addToHistory("history item"); + editor.setAutocompleteProvider(provider); + editor.setPaddingX(4); + editor.setAutocompleteMaxVisible(8); + editor.invalidate(); + + assert.deepEqual(delegate.setTextCalls, ["prefill"]); + assert.deepEqual(delegate.insertedTexts, [" plus"]); + assert.equal(expandedText, "expanded delegate text"); + assert.deepEqual(delegate.expandedTextCalls, [1]); + assert.deepEqual(delegate.historyAdds, ["history item"]); + assert.deepEqual(delegate.autocompleteProviders, [provider]); + assert.deepEqual(delegate.paddingXValues, [4]); + assert.deepEqual(delegate.autocompleteMaxVisibleValues, [8]); + assert.deepEqual(delegate.invalidations, [1]); + assert.equal(editor.getText(), delegate.getText()); + }); + + it("falls back to delegate getText when expanded-text method is absent", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(); + delegate.setText("plain delegate text"); + Object.defineProperty(delegate, "getExpandedText", { + value: undefined, + configurable: true, + }); + setInsertDelegateForTest(editor, delegate); + + assert.equal(editor.getExpandedText(), "plain delegate text"); + }); + + it("syncs callbacks and public flags before delegate input", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createStockSurfaceDelegateEditor(); + const changedTexts: string[] = []; + const onSubmit = () => {}; + const onChange = (text: string) => changedTexts.push(text); + const onEscape = () => {}; + const onCtrlD = () => {}; + const onPasteImage = () => {}; + const onExtensionShortcut = () => false; + const actionHandler = () => {}; + const borderColor = (text: string) => `border:${text}`; + editor.onSubmit = onSubmit; + editor.onChange = onChange; + editor.onEscape = onEscape; + editor.onCtrlD = onCtrlD; + editor.onPasteImage = onPasteImage; + editor.onExtensionShortcut = onExtensionShortcut; + editor.actionHandlers.set( + "app.interrupt" as Parameters[0], + actionHandler, + ); + editor.borderColor = borderColor; + editor.focused = true; + editor.disableSubmit = true; + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("z"); + + const snapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[0]; + assert.ok(snapshot, "expected delegate to record sync state before input"); + assert.equal(snapshot.onSubmit, onSubmit); + assert.equal(snapshot.onEscape, onEscape); + assert.equal(snapshot.onCtrlD, onCtrlD); + assert.equal(snapshot.onPasteImage, onPasteImage); + assert.equal(snapshot.onExtensionShortcut, onExtensionShortcut); + assert.equal(snapshot.actionHandlers instanceof Map, true); + assert.equal( + (snapshot.actionHandlers as Map).get("app.interrupt"), + actionHandler, + ); + assert.equal(snapshot.borderColor, borderColor); + assert.equal(snapshot.focused, true); + assert.equal(snapshot.disableSubmit, true); + assert.equal(snapshot.onChange, editor.onChange); + assert.notEqual(snapshot.onChange, onChange); + assert.deepEqual(changedTexts, ["z"]); + }); + + it("chains delegate submit and change callbacks without duplicating on resync", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createStockSurfaceDelegateEditor(); + const calls: string[] = []; + const editorOnSubmit = (text: string) => calls.push(`editor submit:${text}`); + const delegateOnSubmit = (text: string) => calls.push(`delegate submit:${text}`); + const editorOnChange = (text: string) => calls.push(`editor change:${text}`); + const delegateOnChange = (text: string) => calls.push(`delegate change:${text}`); + + editor.onSubmit = editorOnSubmit; + editor.onChange = editorOnChange; + delegate.onSubmit = delegateOnSubmit; + delegate.onChange = delegateOnChange; + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("z"); + + const firstSnapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[0]; + assert.ok(firstSnapshot, "expected delegate to record first sync state before input"); + assert.notEqual(firstSnapshot.onSubmit, editorOnSubmit); + assert.notEqual(firstSnapshot.onSubmit, delegateOnSubmit); + assert.notEqual(firstSnapshot.onChange, editor.onChange); + assert.notEqual(firstSnapshot.onChange, delegateOnChange); + + (firstSnapshot.onSubmit as (text: string) => void)("sent"); + editor.handleInput("q"); + + const secondSnapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[1]; + assert.ok(secondSnapshot, "expected delegate to record second sync state before input"); + (secondSnapshot.onSubmit as (text: string) => void)("again"); + + assert.deepEqual(calls, [ + "editor change:z", + "delegate change:z", + "editor submit:sent", + "delegate submit:sent", + "editor change:zq", + "delegate change:zq", + "editor submit:again", + "delegate submit:again", + ]); + }); + + it("chains delegate shortcut fields and merges action handlers", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createStockSurfaceDelegateEditor(); + const shortcutCalls: string[] = []; + const delegateOnEscape = () => shortcutCalls.push("delegate escape"); + const delegateOnCtrlD = () => shortcutCalls.push("delegate ctrl-d"); + const delegateOnPasteImage = () => shortcutCalls.push("delegate paste-image"); + const delegateOnExtensionShortcut = (data: string) => { + shortcutCalls.push(`delegate shortcut:${data}`); + return data === "delegate-handled"; + }; + const delegateActionHandler = () => {}; + const editorActionHandler = () => {}; + const editorOnEscape = () => shortcutCalls.push("editor escape"); + const editorOnCtrlD = () => shortcutCalls.push("editor ctrl-d"); + const editorOnPasteImage = () => shortcutCalls.push("editor paste-image"); + const editorOnExtensionShortcut = (data: string) => { + shortcutCalls.push(`editor shortcut:${data}`); + return data === "editor-handled"; + }; + + delegate.onEscape = delegateOnEscape; + delegate.onCtrlD = delegateOnCtrlD; + delegate.onPasteImage = delegateOnPasteImage; + delegate.onExtensionShortcut = delegateOnExtensionShortcut; + delegate.actionHandlers.set( + "app.exit" as Parameters[0], + delegateActionHandler, + ); + editor.onEscape = editorOnEscape; + editor.onCtrlD = editorOnCtrlD; + editor.onPasteImage = editorOnPasteImage; + editor.onExtensionShortcut = editorOnExtensionShortcut; + editor.actionHandlers.set( + "app.interrupt" as Parameters[0], + editorActionHandler, + ); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("z"); + + const snapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[0]; + assert.ok(snapshot, "expected delegate to record sync state before input"); + assert.notEqual(snapshot.onEscape, delegateOnEscape); + assert.notEqual(snapshot.onEscape, editorOnEscape); + assert.notEqual(snapshot.onCtrlD, delegateOnCtrlD); + assert.notEqual(snapshot.onCtrlD, editorOnCtrlD); + assert.notEqual(snapshot.onPasteImage, delegateOnPasteImage); + assert.notEqual(snapshot.onPasteImage, editorOnPasteImage); + assert.notEqual(snapshot.onExtensionShortcut, delegateOnExtensionShortcut); + assert.notEqual(snapshot.onExtensionShortcut, editorOnExtensionShortcut); + assert.deepEqual(shortcutCalls, ["editor shortcut:z", "delegate shortcut:z"]); + + shortcutCalls.length = 0; + (snapshot.onEscape as () => void)(); + (snapshot.onCtrlD as () => void)(); + (snapshot.onPasteImage as () => void)(); + assert.equal((snapshot.onExtensionShortcut as (data: string) => boolean)("editor-handled"), true); + assert.equal((snapshot.onExtensionShortcut as (data: string) => boolean)("delegate-handled"), true); + assert.equal((snapshot.onExtensionShortcut as (data: string) => boolean)("not-handled"), false); + assert.deepEqual(shortcutCalls, [ + "editor escape", + "delegate escape", + "editor ctrl-d", + "delegate ctrl-d", + "editor paste-image", + "delegate paste-image", + "editor shortcut:editor-handled", + "editor shortcut:delegate-handled", + "delegate shortcut:delegate-handled", + "editor shortcut:not-handled", + "delegate shortcut:not-handled", + ]); + assert.equal(snapshot.actionHandlers instanceof Map, true); + assert.notEqual(snapshot.actionHandlers, editor.actionHandlers); + assert.equal( + (snapshot.actionHandlers as Map).get("app.exit"), + delegateActionHandler, + ); + assert.equal( + (snapshot.actionHandlers as Map).get("app.interrupt"), + editorActionHandler, + ); + }); + + it("removes stale copied action handlers without deleting delegate-owned entries", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createStockSurfaceDelegateEditor(); + const outerOnlyAction = "pi-vim.outer" as Parameters[0]; + const delegateOnlyAction = "pi-vim.delegate" as Parameters[0]; + const delegateReplacementAction = "pi-vim.delegate-replacement" as Parameters[0]; + const editorOnlyHandler = () => {}; + const editorReplacedHandler = () => {}; + const delegateOnlyHandler = () => {}; + const delegateReplacementHandler = () => {}; + + delegate.actionHandlers.set(delegateOnlyAction, delegateOnlyHandler); + editor.actionHandlers.set(outerOnlyAction, editorOnlyHandler); + editor.actionHandlers.set(delegateReplacementAction, editorReplacedHandler); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("z"); + + const firstSnapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[0]; + assert.ok(firstSnapshot, "expected delegate to record first sync state before input"); + const firstHandlers = firstSnapshot.actionHandlers as Map; + assert.equal(firstHandlers.get(outerOnlyAction), editorOnlyHandler); + assert.equal(firstHandlers.get(delegateReplacementAction), editorReplacedHandler); + assert.equal(firstHandlers.get(delegateOnlyAction), delegateOnlyHandler); + + delegate.actionHandlers.set(delegateReplacementAction, delegateReplacementHandler); + editor.actionHandlers.delete(outerOnlyAction); + editor.actionHandlers.delete(delegateReplacementAction); + editor.handleInput("q"); + + const secondSnapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[1]; + assert.ok(secondSnapshot, "expected delegate to record second sync state before input"); + const secondHandlers = secondSnapshot.actionHandlers as Map; + assert.equal(secondHandlers.has(outerOnlyAction), false); + assert.equal(secondHandlers.get(delegateReplacementAction), delegateReplacementHandler); + assert.equal(secondHandlers.get(delegateOnlyAction), delegateOnlyHandler); + }); + + it("creates a delegate-owned action handler map when the delegate lacks one", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createStockSurfaceDelegateEditor(); + const outerOnlyAction = "pi-vim.outer" as Parameters[0]; + const delegateOnlyAction = "pi-vim.delegate" as Parameters[0]; + const editorOnlyHandler = () => {}; + const delegateOnlyHandler = () => {}; + + (delegate as unknown as { actionHandlers?: unknown }).actionHandlers = undefined; + editor.actionHandlers.set(outerOnlyAction, editorOnlyHandler); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("z"); + + const firstSnapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[0]; + assert.ok(firstSnapshot, "expected delegate to record first sync state before input"); + assert.equal(firstSnapshot.actionHandlers instanceof Map, true); + assert.notEqual(firstSnapshot.actionHandlers, editor.actionHandlers); + const firstHandlers = firstSnapshot.actionHandlers as Map; + assert.equal(firstHandlers.get(outerOnlyAction), editorOnlyHandler); + + firstHandlers.set(delegateOnlyAction, delegateOnlyHandler); + editor.actionHandlers.delete(outerOnlyAction); + editor.handleInput("q"); + + const secondSnapshot: DelegateSyncSnapshot | undefined = delegate.inputSyncSnapshots[1]; + assert.ok(secondSnapshot, "expected delegate to record second sync state before input"); + const secondHandlers = secondSnapshot.actionHandlers as Map; + assert.equal(secondHandlers.has(outerOnlyAction), false); + assert.equal(secondHandlers.get(delegateOnlyAction), delegateOnlyHandler); + assert.equal(editor.actionHandlers.has(delegateOnlyAction), false); + }); + + it("routes NORMAL escape through the outer interrupt when the delegate has onEscape", () => { + const interruptKeybindings = { + matches(data: string, action: string): boolean { + return data === "\x1b" && action === "app.interrupt"; + }, + } as unknown as ConstructorParameters[2]; + const editor = new ModalEditor(stubTui, stubTheme, interruptKeybindings); + const delegate = new CompatibleDelegateEditor(stubTui, stubTheme, interruptKeybindings); + const calls: string[] = []; + + delegate.onEscape = () => calls.push("delegate"); + editor.onEscape = () => calls.push("outer"); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("\x1b"); + assert.equal(editor.getMode(), "normal"); + assert.deepEqual(calls, []); + + editor.handleInput("\x1b"); + assert.deepEqual(calls, ["outer", "delegate"]); + }); +}); + +describe("ModalEditor delegate render overlay", () => { + it("renders delegate output with INSERT mode label overlay", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor([ + "delegate first line", + "delegate footer 1234567890", + ]); + editor.focused = true; + setInsertDelegateForTest(editor, delegate); + + const lines = editor.render(24); + + const footer = lines.at(-1) ?? ""; + assert.equal(lines[0], "delegate first line"); + assert.equal(footer.includes("delegate footer"), true); + assert.equal(footer.endsWith(" INSERT "), true); + assert.equal(delegate.focused, true); + assert.equal(visibleWidth(footer), 24); + }); + + it("renders NORMAL label after escape over delegate output", () => { + const editor = new ModalEditor(stubTui, stubTheme, stubKeybindings); + const delegate = createCompatibleDelegateEditor(["delegate normal footer"]); + setInsertDelegateForTest(editor, delegate); + + editor.handleInput("\x1b"); + const lines = editor.render(30); + + assert.equal(editor.getMode(), "normal"); + assert.equal(lines.at(-1), "delegate normal footer NORMAL "); + assert.equal(visibleWidth(lines.at(-1) ?? ""), 30); + }); +}); + +describe("insert delegate factory integration", () => { + function createMalformedStateDelegate( + mutate: (state: { lines: unknown[]; cursorLine: unknown; cursorCol: unknown }) => void, + ): CompatibleDelegateEditor { + const editor = createCompatibleDelegateEditor(); + const state = (editor as unknown as { + state: { lines: unknown[]; cursorLine: unknown; cursorCol: unknown }; + }).state; + mutate(state); + return editor; + } + + it("captures the previous factory before install and calls it once per mounted editor", async () => { + let previousFactoryCalls = 0; + const previousEditors: CompatibleDelegateEditor[] = []; + const previousFactory: PreviousEditorFactory = () => { + previousFactoryCalls++; + const previousEditor = createCompatibleDelegateEditor(); + previousEditors.push(previousEditor); + return previousEditor; + }; + + const extension = await installExtensionWithEditorFactory(previousFactory); + + assert.equal(extension.getEditorComponentCalls, 1); + assert.deepEqual(extension.uiCallOrder, ["getEditorComponent", "setEditorComponent"]); + + const first = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + const second = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + + assert.equal(first instanceof ModalEditor, true); + assert.equal(second instanceof ModalEditor, true); + assert.equal(previousFactoryCalls, 2); + assert.equal(previousEditors.length, 2); + assert.equal(extension.notificationCalls, 0); + }); + + + it("warns and falls back when the previous factory throws", async () => { + const extension = await installExtensionWithEditorFactory(() => { + throw new Error("previous failed"); + }); + + const editor = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + + assert.equal(editor instanceof ModalEditor, true); + assert.equal(editor.getMode(), "insert"); + assert.equal(extension.notificationCalls, 1); + assert.match(extension.notifications[0]?.message ?? "", /previous editor factory failed/i); + assert.match(extension.notifications[0]?.message ?? "", /previous failed/); + assert.equal(extension.notifications[0]?.type, "warning"); + }); + + for (const scenario of [ + { + name: "missing getCursor()", + makeEditor(): unknown { + const editor = createCompatibleDelegateEditor() as CompatibleDelegateEditor; + (editor as unknown as Record).getCursor = undefined; + return editor; + }, + reason: /getCursor/, + }, + { + name: "missing getLines()", + makeEditor(): unknown { + const editor = createCompatibleDelegateEditor() as CompatibleDelegateEditor; + (editor as unknown as Record).getLines = undefined; + return editor; + }, + reason: /getLines/, + }, + { + name: "missing required internals", + makeEditor(): unknown { + return { + ...createMinimalIncompatibleEditor(), + getLines(): string[] { + return [""]; + }, + getCursor(): { line: number; col: number } { + return { line: 0, col: 0 }; + }, + insertTextAtCursor(text: string): void { + void text; + }, + }; + }, + reason: /state|pushUndoSnapshot/, + }, + { + name: "empty state lines", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.lines = []; + }); + }, + reason: /empty state\.lines/i, + }, + { + name: "non-string state line", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.lines = ["ok", 42]; + }); + }, + reason: /bad state\.lines\[1\]/i, + }, + { + name: "NaN cursorLine", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.cursorLine = Number.NaN; + }); + }, + reason: /bad state\.cursorLine/i, + }, + { + name: "Infinity cursorCol", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.cursorCol = Number.POSITIVE_INFINITY; + }); + }, + reason: /bad state\.cursorCol/i, + }, + { + name: "fractional cursorLine", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.cursorLine = 0.5; + }); + }, + reason: /bad state\.cursorLine/i, + }, + { + name: "fractional cursorCol", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.cursorCol = 0.5; + }); + }, + reason: /bad state\.cursorCol/i, + }, + { + name: "cursorLine out of bounds", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.lines = ["only"]; + state.cursorLine = 1; + }); + }, + reason: /bad state\.cursorLine:1\/1/i, + }, + { + name: "cursorCol out of bounds", + makeEditor(): unknown { + return createMalformedStateDelegate((state) => { + state.lines = ["abc"]; + state.cursorCol = 4; + }); + }, + reason: /bad state\.cursorCol:4\/3/i, + }, + ]) { + it(`warns and falls back when the previous editor is incompatible: ${scenario.name}`, async () => { + const extension = await installExtensionWithEditorFactory(() => scenario.makeEditor()); + + const editor = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + + assert.equal(editor instanceof ModalEditor, true); + assert.equal(editor.getMode(), "insert"); + assert.equal(extension.notificationCalls, 1); + assert.equal(extension.notifications[0]?.type, "warning"); + assert.match(extension.notifications[0]?.message ?? "", /incompatible previous editor/i); + assert.match(extension.notifications[0]?.message ?? "", scenario.reason); + }); + } + + it("emits one compatibility warning per mounted editor", async () => { + const extension = await installExtensionWithEditorFactory(() => createMinimalIncompatibleEditor()); + + extension.editorFactory(stubTui, stubTheme, stubKeybindings); + extension.editorFactory(stubTui, stubTheme, stubKeybindings); + + assert.equal(extension.notificationCalls, 2); + assert.equal(extension.notifications.length, 2); + assert.match(extension.notifications[0]?.message ?? "", /incompatible previous editor/i); + assert.match(extension.notifications[1]?.message ?? "", /incompatible previous editor/i); + }); + +}); + +describe("image attachments compatibility contract", () => { + it("preserves image-attachments style behavior when the image editor loads first", async () => { + const bracketedDelegate = createImageAttachmentsStyleDelegate(); + const bracketedExtension = await installExtensionWithEditorFactory(() => bracketedDelegate); + const bracketedEditor = bracketedExtension.editorFactory(stubTui, stubTheme, stubKeybindings); + + bracketedEditor.handleInput(bracketedPaste(IMAGE_ATTACHMENT_CONTRACT_PATH)); + + assert.equal(bracketedEditor.getText(), "[Image #1] "); + assert.deepEqual(bracketedDelegate.widgetCalls, [{ content: ["[Image #1]"] }]); + assert.equal(bracketedEditor.getMode(), "insert"); + + const directDelegate = createImageAttachmentsStyleDelegate(); + const directExtension = await installExtensionWithEditorFactory(() => directDelegate); + const directEditor = directExtension.editorFactory(stubTui, stubTheme, stubKeybindings); + + directEditor.insertTextAtCursor(IMAGE_ATTACHMENT_CONTRACT_PATH); + + assert.equal(directEditor.getText(), "[Image #1] "); + assert.deepEqual(directDelegate.widgetCalls, [{ content: ["[Image #1]"] }]); + assert.equal(directEditor.getMode(), "insert"); + }); + + it("supports image-attachments style decoration when pi-vim loads first", async () => { + const bracketedExtension = await installExtensionWithEditorFactory(); + const bracketed = decorateImageAttachmentsStyleEditor( + bracketedExtension.editorFactory(stubTui, stubTheme, stubKeybindings), + ); + + assertWrappableBaselineSurface(bracketed.editor); + + bracketed.editor.handleInput(bracketedPaste(IMAGE_ATTACHMENT_CONTRACT_PATH)); + + assert.equal(bracketed.editor.getText(), "[Image #1] "); + assert.deepEqual(bracketed.widgetCalls, [{ content: ["[Image #1]"] }]); + assert.equal(bracketed.editor.getMode(), "insert"); + + const directExtension = await installExtensionWithEditorFactory(); + const direct = decorateImageAttachmentsStyleEditor( + directExtension.editorFactory(stubTui, stubTheme, stubKeybindings), + ); + + assertWrappableBaselineSurface(direct.editor); + + direct.editor.insertTextAtCursor(IMAGE_ATTACHMENT_CONTRACT_PATH); + + assert.equal(direct.editor.getText(), "[Image #1] "); + assert.deepEqual(direct.widgetCalls, [{ content: ["[Image #1]"] }]); + assert.equal(direct.editor.getMode(), "insert"); + }); +}); + +describe("pi-vim wrappability", () => { + it("returns the canonical ModalEditor in INSERT mode when no previous factory exists", async () => { + const extension = await installExtensionWithEditorFactory(); + const editor = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + + // With no chain, the canonical class is used so `instanceof ModalEditor` works + // for callers that rely on identity. + assert.equal(editor instanceof ModalEditor, true); + assert.equal(editor.getMode(), "insert"); + assert.equal(extension.notificationCalls, 0); + }); + + it("exposes the baseline method and field surface required by later decorators", async () => { + const extension = await installExtensionWithEditorFactory(); + const editor = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + + assertWrappableBaselineSurface(editor); + }); + + it("supports in-place decoration of the returned instance", async () => { + const extension = await installExtensionWithEditorFactory(); + const editor = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + const interceptedInputs: string[] = []; + const originalHandleInput = editor.handleInput.bind(editor); + const originalRender = editor.render.bind(editor); + + editor.handleInput = (data: string): void => { + interceptedInputs.push(data); + originalHandleInput(data); + }; + editor.render = (width: number): string[] => { + const lines = originalRender(width); + const first = lines[0] ?? ""; + return [`decorated:${first}`, ...lines.slice(1)]; + }; + + const decorated = editor; + + assert.equal(decorated, editor); + assertWrappableBaselineSurface(decorated); + + decorated.handleInput("a"); + + assert.deepEqual(interceptedInputs, ["a"]); + assert.equal(decorated.getText(), "a"); + assert.equal(decorated.getMode(), "insert"); + assert.equal(decorated.render(20)[0]?.startsWith("decorated:"), true); + }); + + it("allows a transparent separate wrapper object to forward through to pi-vim", async () => { + const extension = await installExtensionWithEditorFactory(); + const editor = extension.editorFactory(stubTui, stubTheme, stubKeybindings); + const interceptedInputs: string[] = []; + const wrapper = createTransparentEditorWrapper(editor, interceptedInputs); + const changedTexts: string[] = []; + + assert.notEqual(wrapper, editor); + assertWrappableBaselineSurface(wrapper); + + wrapper.onChange = (text: string) => changedTexts.push(text); + wrapper.focused = true; + wrapper.disableSubmit = true; + + wrapper.handleInput("z"); + + assert.deepEqual(interceptedInputs, ["z"]); + assert.deepEqual(changedTexts, ["z"]); + assert.equal(wrapper.getText(), "z"); + assert.equal(editor.getText(), "z"); + assert.equal(editor.focused, true); + assert.equal(editor.disableSubmit, true); + + wrapper.handleInput("\x1b"); + + assert.deepEqual(interceptedInputs, ["z", "\x1b"]); + assert.equal(wrapper.getMode(), "normal"); + assert.equal(editor.getMode(), "normal"); + assert.equal((wrapper.render(20).at(-1) ?? "").includes(" NORMAL "), true); + }); +}); + describe("cursor shape rendering", () => { it("writes insert cursor shape and strips the EOL software cursor", () => { const tui = createCursorShapeTui({ initialShowHardwareCursor: true }); diff --git a/types.ts b/types.ts index 05a12afb..204f2ab5 100644 --- a/types.ts +++ b/types.ts @@ -1,7 +1,3 @@ -/** - * Types and constants for vim-mode extension - */ - export type Mode = "normal" | "insert"; export type CharMotion = "f" | "F" | "t" | "T"; export type PendingMotion = CharMotion | null; @@ -12,7 +8,6 @@ export interface LastCharMotion { char: string; } -// Normal mode key mappings: key -> escape sequence (or null for mode switch) export const NORMAL_KEYS: Record = { h: "\x1b[D", // left j: "\x1b[B", // down @@ -33,10 +28,8 @@ export const NORMAL_KEYS: Record = { O: null, // open line above }; -// Character motion keys that wait for a target character export const CHAR_MOTION_KEYS = new Set(["f", "F", "t", "T"]); -// Escape sequences export const ESC_LEFT = "\x1b[D"; export const ESC_RIGHT = "\x1b[C"; export const CTRL_A = "\x01"; // line start