@boa-framework/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +20 -0
- package/dist/index.js +21 -0
- package/dist/observability/index.d.ts +2 -0
- package/dist/observability/index.js +2 -0
- package/dist/observability/logger.d.ts +5 -0
- package/dist/observability/logger.js +17 -0
- package/dist/registry/index.d.ts +2 -0
- package/dist/registry/index.js +2 -0
- package/dist/registry/ui-block-registry.d.ts +14 -0
- package/dist/registry/ui-block-registry.js +50 -0
- package/dist/runtime/index.d.ts +6 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/js-runtime.d.ts +10 -0
- package/dist/runtime/js-runtime.js +15 -0
- package/dist/runtime/memoize.d.ts +11 -0
- package/dist/runtime/memoize.js +29 -0
- package/dist/runtime/runtime-registry.d.ts +4 -0
- package/dist/runtime/runtime-registry.js +17 -0
- package/dist/runtime/wasm-runtime.d.ts +6 -0
- package/dist/runtime/wasm-runtime.js +6 -0
- package/dist/sandbox/index.d.ts +4 -0
- package/dist/sandbox/index.js +3 -0
- package/dist/sandbox/permissions.d.ts +9 -0
- package/dist/sandbox/permissions.js +13 -0
- package/dist/sandbox/sandbox.d.ts +5 -0
- package/dist/sandbox/sandbox.js +37 -0
- package/dist/sdk/boa-ui.d.ts +22 -0
- package/dist/sdk/boa-ui.js +59 -0
- package/dist/sdk/hooks.d.ts +16 -0
- package/dist/sdk/hooks.js +55 -0
- package/dist/sdk/index.d.ts +4 -0
- package/dist/sdk/index.js +3 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.js +2 -0
- package/dist/types/ui-block.d.ts +21 -0
- package/dist/types/ui-block.js +2 -0
- package/dist/types/ui-workflow.d.ts +42 -0
- package/dist/types/ui-workflow.js +2 -0
- package/dist/workflows/index.d.ts +4 -0
- package/dist/workflows/index.js +3 -0
- package/dist/workflows/utils/resolve.d.ts +9 -0
- package/dist/workflows/utils/resolve.js +77 -0
- package/dist/workflows/workflow-engine.d.ts +21 -0
- package/dist/workflows/workflow-engine.js +149 -0
- package/package.json +34 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type { UIBlockLayer, UIBlockRuntime, UIBlockManifest, UIBlockModule, UIBlockDef, } from "./types/ui-block.js";
|
|
2
|
+
export type { UIWorkflowDefinition, UIWorkflowStep, UIWorkflowContext, UIWorkflowResult, UIErrorOutput, } from "./types/ui-workflow.js";
|
|
3
|
+
export { JSRuntime } from "./runtime/js-runtime.js";
|
|
4
|
+
export type { IUIRuntimeAdapter } from "./runtime/js-runtime.js";
|
|
5
|
+
export { WASMRuntime } from "./runtime/wasm-runtime.js";
|
|
6
|
+
export { getRuntimeForBlock } from "./runtime/runtime-registry.js";
|
|
7
|
+
export { MemoCache } from "./runtime/memoize.js";
|
|
8
|
+
export { runInSandbox } from "./sandbox/sandbox.js";
|
|
9
|
+
export type { BlockPermissions } from "./sandbox/permissions.js";
|
|
10
|
+
export { PURE_BLOCK_PERMISSIONS, CAPABILITY_BLOCK_PERMISSIONS } from "./sandbox/permissions.js";
|
|
11
|
+
export { UIBlockRegistry } from "./registry/ui-block-registry.js";
|
|
12
|
+
export { UIWorkflowEngine } from "./workflows/workflow-engine.js";
|
|
13
|
+
export type { WorkflowEngineConfig } from "./workflows/workflow-engine.js";
|
|
14
|
+
export { deepResolve, evalCondition } from "./workflows/utils/resolve.js";
|
|
15
|
+
export { createBoaUI } from "./sdk/boa-ui.js";
|
|
16
|
+
export type { BoaUI, BoaUIOptions } from "./sdk/boa-ui.js";
|
|
17
|
+
export { createBoaHooks } from "./sdk/hooks.js";
|
|
18
|
+
export { consoleLogger } from "./observability/logger.js";
|
|
19
|
+
export { parseBlockBoa, parseWorkflowBoa, parseRegistryBoa, parseProjectBoa } from "@boa-framework/core";
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Runtime
|
|
2
|
+
export { JSRuntime } from "./runtime/js-runtime.js";
|
|
3
|
+
export { WASMRuntime } from "./runtime/wasm-runtime.js";
|
|
4
|
+
export { getRuntimeForBlock } from "./runtime/runtime-registry.js";
|
|
5
|
+
export { MemoCache } from "./runtime/memoize.js";
|
|
6
|
+
// Sandbox
|
|
7
|
+
export { runInSandbox } from "./sandbox/sandbox.js";
|
|
8
|
+
export { PURE_BLOCK_PERMISSIONS, CAPABILITY_BLOCK_PERMISSIONS } from "./sandbox/permissions.js";
|
|
9
|
+
// Registry
|
|
10
|
+
export { UIBlockRegistry } from "./registry/ui-block-registry.js";
|
|
11
|
+
// Workflow Engine
|
|
12
|
+
export { UIWorkflowEngine } from "./workflows/workflow-engine.js";
|
|
13
|
+
export { deepResolve, evalCondition } from "./workflows/utils/resolve.js";
|
|
14
|
+
// SDK
|
|
15
|
+
export { createBoaUI } from "./sdk/boa-ui.js";
|
|
16
|
+
export { createBoaHooks } from "./sdk/hooks.js";
|
|
17
|
+
// Observability
|
|
18
|
+
export { consoleLogger } from "./observability/logger.js";
|
|
19
|
+
// Re-export backend parser functions used for .boa registration
|
|
20
|
+
export { parseBlockBoa, parseWorkflowBoa, parseRegistryBoa, parseProjectBoa } from "@boa-framework/core";
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WorkflowEngineConfig } from "../workflows/workflow-engine.js";
|
|
2
|
+
type LoggerCallbacks = Pick<WorkflowEngineConfig, "onStepStart" | "onStepEnd" | "onStepSkip" | "onStepError">;
|
|
3
|
+
export declare function consoleLogger(prefix?: string): LoggerCallbacks;
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function consoleLogger(prefix = "[boa-ui]") {
|
|
2
|
+
return {
|
|
3
|
+
onStepStart(stepId, input) {
|
|
4
|
+
console.log(`${prefix} STEP START: ${stepId}`, input);
|
|
5
|
+
},
|
|
6
|
+
onStepEnd(stepId, output, durationMs) {
|
|
7
|
+
console.log(`${prefix} STEP END: ${stepId} (${durationMs.toFixed(1)}ms)`, output);
|
|
8
|
+
},
|
|
9
|
+
onStepSkip(stepId, reason) {
|
|
10
|
+
console.log(`${prefix} STEP SKIP: ${stepId} — ${reason}`);
|
|
11
|
+
},
|
|
12
|
+
onStepError(stepId, error) {
|
|
13
|
+
console.error(`${prefix} STEP ERROR: ${stepId}`, error);
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { UIBlockDef } from "../types/ui-block.js";
|
|
2
|
+
import type { UIWorkflowDefinition } from "../types/ui-workflow.js";
|
|
3
|
+
export declare class UIBlockRegistry {
|
|
4
|
+
private blocks;
|
|
5
|
+
private workflows;
|
|
6
|
+
registerBlock(blockDef: UIBlockDef): void;
|
|
7
|
+
registerBlockFromBoa(boaText: string, entry: string): void;
|
|
8
|
+
registerWorkflow(workflow: UIWorkflowDefinition): void;
|
|
9
|
+
getBlock(nameOrRef: string): UIBlockDef;
|
|
10
|
+
getWorkflow(name: string): UIWorkflowDefinition;
|
|
11
|
+
listBlocks(): UIBlockDef[];
|
|
12
|
+
listWorkflows(): UIWorkflowDefinition[];
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=ui-block-registry.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parseBlockBoa } from "@boa-framework/core";
|
|
2
|
+
export class UIBlockRegistry {
|
|
3
|
+
blocks = new Map();
|
|
4
|
+
workflows = new Map();
|
|
5
|
+
registerBlock(blockDef) {
|
|
6
|
+
this.blocks.set(`${blockDef.name}@${blockDef.version}`, blockDef);
|
|
7
|
+
}
|
|
8
|
+
registerBlockFromBoa(boaText, entry) {
|
|
9
|
+
const parsed = parseBlockBoa(boaText);
|
|
10
|
+
const blockDef = {
|
|
11
|
+
name: parsed.name,
|
|
12
|
+
version: parsed.version,
|
|
13
|
+
layer: parsed.layer,
|
|
14
|
+
runtime: (parsed.runtime === "node" ? "js" : parsed.runtime),
|
|
15
|
+
entry,
|
|
16
|
+
manifest: {
|
|
17
|
+
...parsed,
|
|
18
|
+
layer: parsed.layer,
|
|
19
|
+
runtime: (parsed.runtime === "node" ? "js" : parsed.runtime),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
this.registerBlock(blockDef);
|
|
23
|
+
}
|
|
24
|
+
registerWorkflow(workflow) {
|
|
25
|
+
this.workflows.set(workflow.name, workflow);
|
|
26
|
+
}
|
|
27
|
+
getBlock(nameOrRef) {
|
|
28
|
+
const exact = this.blocks.get(nameOrRef);
|
|
29
|
+
if (exact)
|
|
30
|
+
return exact;
|
|
31
|
+
for (const [ref, def] of this.blocks) {
|
|
32
|
+
if (ref.startsWith(nameOrRef + "@") || def.name === nameOrRef)
|
|
33
|
+
return def;
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`UI block not found: "${nameOrRef}"`);
|
|
36
|
+
}
|
|
37
|
+
getWorkflow(name) {
|
|
38
|
+
const wf = this.workflows.get(name);
|
|
39
|
+
if (!wf)
|
|
40
|
+
throw new Error(`UI workflow not found: "${name}"`);
|
|
41
|
+
return wf;
|
|
42
|
+
}
|
|
43
|
+
listBlocks() {
|
|
44
|
+
return Array.from(this.blocks.values());
|
|
45
|
+
}
|
|
46
|
+
listWorkflows() {
|
|
47
|
+
return Array.from(this.workflows.values());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=ui-block-registry.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { JSRuntime } from "./js-runtime.js";
|
|
2
|
+
export type { IUIRuntimeAdapter } from "./js-runtime.js";
|
|
3
|
+
export { WASMRuntime } from "./wasm-runtime.js";
|
|
4
|
+
export { getRuntimeForBlock } from "./runtime-registry.js";
|
|
5
|
+
export { MemoCache } from "./memoize.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { UIBlockDef } from "../types/ui-block.js";
|
|
2
|
+
export interface IUIRuntimeAdapter {
|
|
3
|
+
execute(blockDef: UIBlockDef, input: Record<string, unknown>): Promise<unknown>;
|
|
4
|
+
}
|
|
5
|
+
export declare class JSRuntime implements IUIRuntimeAdapter {
|
|
6
|
+
private moduleCache;
|
|
7
|
+
execute(blockDef: UIBlockDef, input: Record<string, unknown>): Promise<unknown>;
|
|
8
|
+
clearCache(): void;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=js-runtime.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class JSRuntime {
|
|
2
|
+
moduleCache = new Map();
|
|
3
|
+
async execute(blockDef, input) {
|
|
4
|
+
let mod = this.moduleCache.get(blockDef.entry);
|
|
5
|
+
if (!mod) {
|
|
6
|
+
mod = await import(/* @vite-ignore */ blockDef.entry);
|
|
7
|
+
this.moduleCache.set(blockDef.entry, mod);
|
|
8
|
+
}
|
|
9
|
+
return mod.default(input);
|
|
10
|
+
}
|
|
11
|
+
clearCache() {
|
|
12
|
+
this.moduleCache.clear();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=js-runtime.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class MemoCache {
|
|
2
|
+
private cache;
|
|
3
|
+
private maxEntries;
|
|
4
|
+
constructor(maxEntries?: number);
|
|
5
|
+
private makeKey;
|
|
6
|
+
get(blockName: string, input: Record<string, unknown>): unknown | undefined;
|
|
7
|
+
set(blockName: string, input: Record<string, unknown>, output: unknown): void;
|
|
8
|
+
clear(): void;
|
|
9
|
+
get size(): number;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=memoize.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export class MemoCache {
|
|
2
|
+
cache = new Map();
|
|
3
|
+
maxEntries;
|
|
4
|
+
constructor(maxEntries = 100) {
|
|
5
|
+
this.maxEntries = maxEntries;
|
|
6
|
+
}
|
|
7
|
+
makeKey(blockName, input) {
|
|
8
|
+
return blockName + ":" + JSON.stringify(input);
|
|
9
|
+
}
|
|
10
|
+
get(blockName, input) {
|
|
11
|
+
const entry = this.cache.get(this.makeKey(blockName, input));
|
|
12
|
+
return entry?.output;
|
|
13
|
+
}
|
|
14
|
+
set(blockName, input, output) {
|
|
15
|
+
if (this.cache.size >= this.maxEntries) {
|
|
16
|
+
const oldest = this.cache.keys().next().value;
|
|
17
|
+
if (oldest)
|
|
18
|
+
this.cache.delete(oldest);
|
|
19
|
+
}
|
|
20
|
+
this.cache.set(this.makeKey(blockName, input), { output, timestamp: Date.now() });
|
|
21
|
+
}
|
|
22
|
+
clear() {
|
|
23
|
+
this.cache.clear();
|
|
24
|
+
}
|
|
25
|
+
get size() {
|
|
26
|
+
return this.cache.size;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=memoize.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { JSRuntime } from "./js-runtime.js";
|
|
2
|
+
import { WASMRuntime } from "./wasm-runtime.js";
|
|
3
|
+
const instances = new Map();
|
|
4
|
+
export function getRuntimeForBlock(runtime) {
|
|
5
|
+
let instance = instances.get(runtime);
|
|
6
|
+
if (!instance) {
|
|
7
|
+
if (runtime === "js")
|
|
8
|
+
instance = new JSRuntime();
|
|
9
|
+
else if (runtime === "wasm")
|
|
10
|
+
instance = new WASMRuntime();
|
|
11
|
+
else
|
|
12
|
+
throw new Error(`No UI runtime for "${runtime}"`);
|
|
13
|
+
instances.set(runtime, instance);
|
|
14
|
+
}
|
|
15
|
+
return instance;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=runtime-registry.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { UIBlockDef } from "../types/ui-block.js";
|
|
2
|
+
import type { IUIRuntimeAdapter } from "./js-runtime.js";
|
|
3
|
+
export declare class WASMRuntime implements IUIRuntimeAdapter {
|
|
4
|
+
execute(_blockDef: UIBlockDef, _input: Record<string, unknown>): Promise<unknown>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=wasm-runtime.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface BlockPermissions {
|
|
2
|
+
allowFetch: boolean;
|
|
3
|
+
allowStorage: boolean;
|
|
4
|
+
allowClipboard: boolean;
|
|
5
|
+
allowDom: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const PURE_BLOCK_PERMISSIONS: BlockPermissions;
|
|
8
|
+
export declare const CAPABILITY_BLOCK_PERMISSIONS: BlockPermissions;
|
|
9
|
+
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const PURE_BLOCK_PERMISSIONS = {
|
|
2
|
+
allowFetch: false,
|
|
3
|
+
allowStorage: false,
|
|
4
|
+
allowClipboard: false,
|
|
5
|
+
allowDom: false,
|
|
6
|
+
};
|
|
7
|
+
export const CAPABILITY_BLOCK_PERMISSIONS = {
|
|
8
|
+
allowFetch: true,
|
|
9
|
+
allowStorage: true,
|
|
10
|
+
allowClipboard: true,
|
|
11
|
+
allowDom: false,
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=permissions.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { IUIRuntimeAdapter } from "../runtime/js-runtime.js";
|
|
2
|
+
import type { UIBlockDef, UIBlockLayer } from "../types/ui-block.js";
|
|
3
|
+
import type { MemoCache } from "../runtime/memoize.js";
|
|
4
|
+
export declare function runInSandbox(runtime: IUIRuntimeAdapter, blockDef: UIBlockDef, input: Record<string, unknown>, blockType: UIBlockLayer, memoCache?: MemoCache): Promise<unknown>;
|
|
5
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export async function runInSandbox(runtime, blockDef, input, blockType, memoCache) {
|
|
2
|
+
if (blockType === "ui-block") {
|
|
3
|
+
// Check memo cache first
|
|
4
|
+
if (memoCache) {
|
|
5
|
+
const cached = memoCache.get(blockDef.name, input);
|
|
6
|
+
if (cached !== undefined)
|
|
7
|
+
return cached;
|
|
8
|
+
}
|
|
9
|
+
const frozenInput = deepFreeze({ ...input });
|
|
10
|
+
const globalObj = typeof globalThis !== "undefined" ? globalThis : {};
|
|
11
|
+
const beforeKeys = new Set(Object.keys(globalObj));
|
|
12
|
+
const result = await runtime.execute(blockDef, frozenInput);
|
|
13
|
+
const afterKeys = Object.keys(globalObj);
|
|
14
|
+
const newKeys = afterKeys.filter((k) => !beforeKeys.has(k));
|
|
15
|
+
if (newKeys.length > 0) {
|
|
16
|
+
throw new Error(`Pure block "${blockDef.name}" modified global state. New globals: ${newKeys.join(", ")}`);
|
|
17
|
+
}
|
|
18
|
+
// Store in memo cache
|
|
19
|
+
if (memoCache)
|
|
20
|
+
memoCache.set(blockDef.name, input, result);
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
// Capability block — side effects allowed
|
|
24
|
+
return runtime.execute(blockDef, input);
|
|
25
|
+
}
|
|
26
|
+
function deepFreeze(obj) {
|
|
27
|
+
if (obj === null || typeof obj !== "object")
|
|
28
|
+
return obj;
|
|
29
|
+
Object.freeze(obj);
|
|
30
|
+
for (const value of Object.values(obj)) {
|
|
31
|
+
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
32
|
+
deepFreeze(value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { UIBlockRegistry } from "../registry/ui-block-registry.js";
|
|
2
|
+
import { UIWorkflowEngine } from "../workflows/workflow-engine.js";
|
|
3
|
+
import type { WorkflowEngineConfig } from "../workflows/workflow-engine.js";
|
|
4
|
+
import type { UIBlockDef } from "../types/ui-block.js";
|
|
5
|
+
import type { UIWorkflowDefinition, UIWorkflowResult } from "../types/ui-workflow.js";
|
|
6
|
+
export interface BoaUIOptions extends Omit<WorkflowEngineConfig, "memoCache"> {
|
|
7
|
+
maxMemoEntries?: number;
|
|
8
|
+
blocks?: UIBlockDef[];
|
|
9
|
+
workflows?: UIWorkflowDefinition[];
|
|
10
|
+
}
|
|
11
|
+
export declare function createBoaUI(options?: BoaUIOptions): {
|
|
12
|
+
run(workflowName: string, input?: Record<string, unknown>): Promise<UIWorkflowResult>;
|
|
13
|
+
registerBlock(blockDef: UIBlockDef): void;
|
|
14
|
+
registerWorkflow(workflow: UIWorkflowDefinition): void;
|
|
15
|
+
registerBlockFromBoa(boaText: string, entry: string): void;
|
|
16
|
+
registerWorkflowFromBoa(boaText: string): void;
|
|
17
|
+
clearMemoCache(): void;
|
|
18
|
+
registry: UIBlockRegistry;
|
|
19
|
+
engine: UIWorkflowEngine;
|
|
20
|
+
};
|
|
21
|
+
export type BoaUI = ReturnType<typeof createBoaUI>;
|
|
22
|
+
//# sourceMappingURL=boa-ui.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { UIBlockRegistry } from "../registry/ui-block-registry.js";
|
|
2
|
+
import { MemoCache } from "../runtime/memoize.js";
|
|
3
|
+
import { UIWorkflowEngine } from "../workflows/workflow-engine.js";
|
|
4
|
+
import { parseWorkflowBoa } from "@boa-framework/core";
|
|
5
|
+
export function createBoaUI(options = {}) {
|
|
6
|
+
const registry = new UIBlockRegistry();
|
|
7
|
+
const memoCache = new MemoCache(options.maxMemoEntries ?? 100);
|
|
8
|
+
const engine = new UIWorkflowEngine(registry, { ...options, memoCache });
|
|
9
|
+
// Pre-register blocks and workflows from options
|
|
10
|
+
if (options.blocks) {
|
|
11
|
+
for (const block of options.blocks) {
|
|
12
|
+
registry.registerBlock(block);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (options.workflows) {
|
|
16
|
+
for (const wf of options.workflows) {
|
|
17
|
+
registry.registerWorkflow(wf);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
run(workflowName, input = {}) {
|
|
22
|
+
return engine.run(workflowName, input);
|
|
23
|
+
},
|
|
24
|
+
registerBlock(blockDef) {
|
|
25
|
+
registry.registerBlock(blockDef);
|
|
26
|
+
},
|
|
27
|
+
registerWorkflow(workflow) {
|
|
28
|
+
registry.registerWorkflow(workflow);
|
|
29
|
+
},
|
|
30
|
+
registerBlockFromBoa(boaText, entry) {
|
|
31
|
+
registry.registerBlockFromBoa(boaText, entry);
|
|
32
|
+
},
|
|
33
|
+
registerWorkflowFromBoa(boaText) {
|
|
34
|
+
const parsed = parseWorkflowBoa(boaText);
|
|
35
|
+
const wfDef = {
|
|
36
|
+
name: parsed.workflow,
|
|
37
|
+
version: parsed.version,
|
|
38
|
+
description: parsed.description,
|
|
39
|
+
config: parsed.config,
|
|
40
|
+
onError: parsed.onErrorBlock ? { block: parsed.onErrorBlock } : undefined,
|
|
41
|
+
steps: parsed.steps.map((s) => ({
|
|
42
|
+
id: s.id ?? s.block,
|
|
43
|
+
block: s.sub ? undefined : s.block,
|
|
44
|
+
sub: s.sub,
|
|
45
|
+
input: s.inputMap,
|
|
46
|
+
when: s.condition,
|
|
47
|
+
parallel: s.parallel,
|
|
48
|
+
})),
|
|
49
|
+
};
|
|
50
|
+
registry.registerWorkflow(wfDef);
|
|
51
|
+
},
|
|
52
|
+
clearMemoCache() {
|
|
53
|
+
memoCache.clear();
|
|
54
|
+
},
|
|
55
|
+
registry,
|
|
56
|
+
engine,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=boa-ui.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BoaUI } from "./boa-ui.js";
|
|
2
|
+
import type { UIWorkflowResult } from "../types/ui-workflow.js";
|
|
3
|
+
/**
|
|
4
|
+
* Creates React hooks for BOA UI workflows.
|
|
5
|
+
* Uses lazy React import so the core library doesn't depend on React.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createBoaHooks(boa: BoaUI): {
|
|
8
|
+
useWorkflow(workflowName: string, input?: Record<string, unknown>): {
|
|
9
|
+
result: UIWorkflowResult | null;
|
|
10
|
+
loading: boolean;
|
|
11
|
+
error: Error | null;
|
|
12
|
+
run: any;
|
|
13
|
+
};
|
|
14
|
+
ensureReact: () => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates React hooks for BOA UI workflows.
|
|
3
|
+
* Uses lazy React import so the core library doesn't depend on React.
|
|
4
|
+
*/
|
|
5
|
+
export function createBoaHooks(boa) {
|
|
6
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7
|
+
let R = null;
|
|
8
|
+
async function ensureReact() {
|
|
9
|
+
if (!R) {
|
|
10
|
+
try {
|
|
11
|
+
R = await Function("return import('react')")();
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new Error("React is required for createBoaHooks(). Install react as a dependency.");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
useWorkflow(workflowName, input) {
|
|
20
|
+
if (!R) {
|
|
21
|
+
throw new Error("Call await ensureReact() before using hooks, or import react before createBoaHooks().");
|
|
22
|
+
}
|
|
23
|
+
const { useState, useEffect, useCallback } = R;
|
|
24
|
+
const [result, setResult] = useState(null);
|
|
25
|
+
const [loading, setLoading] = useState(false);
|
|
26
|
+
const [error, setError] = useState(null);
|
|
27
|
+
const run = useCallback(async (overrideInput) => {
|
|
28
|
+
setLoading(true);
|
|
29
|
+
setError(null);
|
|
30
|
+
try {
|
|
31
|
+
const res = await boa.run(workflowName, overrideInput ?? input ?? {});
|
|
32
|
+
setResult(res);
|
|
33
|
+
return res;
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
37
|
+
setError(e);
|
|
38
|
+
throw e;
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
setLoading(false);
|
|
42
|
+
}
|
|
43
|
+
}, [workflowName, input]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (input) {
|
|
46
|
+
run();
|
|
47
|
+
}
|
|
48
|
+
}, []);
|
|
49
|
+
return { result, loading, error, run };
|
|
50
|
+
},
|
|
51
|
+
ensureReact,
|
|
52
|
+
};
|
|
53
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { UIBlockLayer, UIBlockRuntime, UIBlockManifest, UIBlockModule, UIBlockDef, } from "./ui-block.js";
|
|
2
|
+
export type { UIWorkflowDefinition, UIWorkflowStep, UIWorkflowContext, UIWorkflowResult, UIErrorOutput, } from "./ui-workflow.js";
|
|
3
|
+
export type { BlockManifest, BlockFixture, BlockLayer, BlockRuntime, BlockFieldSchema, } from "@boa-framework/core";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BlockManifest, BlockFixture } from "@boa-framework/core";
|
|
2
|
+
export type UIBlockLayer = "ui-block" | "ui-capability";
|
|
3
|
+
export type UIBlockRuntime = "js" | "wasm";
|
|
4
|
+
export interface UIBlockManifest extends Omit<BlockManifest, "layer" | "runtime"> {
|
|
5
|
+
layer: UIBlockLayer;
|
|
6
|
+
runtime: UIBlockRuntime;
|
|
7
|
+
mocks: BlockFixture[];
|
|
8
|
+
}
|
|
9
|
+
export interface UIBlockModule {
|
|
10
|
+
default: (input: Record<string, unknown>) => unknown | Promise<unknown>;
|
|
11
|
+
}
|
|
12
|
+
export interface UIBlockDef {
|
|
13
|
+
name: string;
|
|
14
|
+
version: string;
|
|
15
|
+
layer: UIBlockLayer;
|
|
16
|
+
runtime: UIBlockRuntime;
|
|
17
|
+
entry: string;
|
|
18
|
+
wasmPath?: string;
|
|
19
|
+
manifest: UIBlockManifest;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=ui-block.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface UIWorkflowDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
config?: Record<string, unknown>;
|
|
6
|
+
steps: UIWorkflowStep[];
|
|
7
|
+
onError?: {
|
|
8
|
+
block: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface UIWorkflowStep {
|
|
12
|
+
id: string;
|
|
13
|
+
block?: string;
|
|
14
|
+
capability?: string;
|
|
15
|
+
sub?: string;
|
|
16
|
+
input?: Record<string, string>;
|
|
17
|
+
when?: string;
|
|
18
|
+
parallel?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface UIWorkflowContext {
|
|
21
|
+
input: Record<string, unknown>;
|
|
22
|
+
steps: Record<string, {
|
|
23
|
+
output: unknown;
|
|
24
|
+
}>;
|
|
25
|
+
config: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
export interface UIWorkflowResult {
|
|
28
|
+
success: boolean;
|
|
29
|
+
steps: Record<string, {
|
|
30
|
+
output: unknown;
|
|
31
|
+
}>;
|
|
32
|
+
error?: UIErrorOutput;
|
|
33
|
+
durationMs: number;
|
|
34
|
+
}
|
|
35
|
+
export interface UIErrorOutput {
|
|
36
|
+
step: string;
|
|
37
|
+
userMessage: string;
|
|
38
|
+
code: string;
|
|
39
|
+
recoverable: boolean;
|
|
40
|
+
originalError?: unknown;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=ui-workflow.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { UIWorkflowContext } from "../../types/ui-workflow.js";
|
|
2
|
+
export declare function deepResolve(inputMap: Record<string, string>, context: UIWorkflowContext): Record<string, unknown>;
|
|
3
|
+
/**
|
|
4
|
+
* Evaluate a simple condition expression against workflow context.
|
|
5
|
+
* Supports: $path.to.value > number, $path.to.value < number,
|
|
6
|
+
* $path.to.value === value, $path.to.value !== value
|
|
7
|
+
*/
|
|
8
|
+
export declare function evalCondition(condition: string, context: UIWorkflowContext): boolean;
|
|
9
|
+
//# sourceMappingURL=resolve.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export function deepResolve(inputMap, context) {
|
|
2
|
+
const resolved = {};
|
|
3
|
+
for (const [key, pathExpr] of Object.entries(inputMap)) {
|
|
4
|
+
if (typeof pathExpr === "string" && pathExpr.startsWith("$")) {
|
|
5
|
+
resolved[key] = resolveReference(pathExpr, context);
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
resolved[key] = pathExpr;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return resolved;
|
|
12
|
+
}
|
|
13
|
+
function resolveReference(path, context) {
|
|
14
|
+
const segments = path.slice(1).split(".");
|
|
15
|
+
let current = context;
|
|
16
|
+
for (const segment of segments) {
|
|
17
|
+
if (current === null || current === undefined || typeof current !== "object")
|
|
18
|
+
return undefined;
|
|
19
|
+
current = current[segment];
|
|
20
|
+
}
|
|
21
|
+
return current;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Evaluate a simple condition expression against workflow context.
|
|
25
|
+
* Supports: $path.to.value > number, $path.to.value < number,
|
|
26
|
+
* $path.to.value === value, $path.to.value !== value
|
|
27
|
+
*/
|
|
28
|
+
export function evalCondition(condition, context) {
|
|
29
|
+
const operators = ["!==", "===", ">=", "<=", "!=", "==", ">", "<"];
|
|
30
|
+
for (const op of operators) {
|
|
31
|
+
const idx = condition.indexOf(op);
|
|
32
|
+
if (idx === -1)
|
|
33
|
+
continue;
|
|
34
|
+
const left = condition.slice(0, idx).trim();
|
|
35
|
+
const right = condition.slice(idx + op.length).trim();
|
|
36
|
+
const leftVal = left.startsWith("$") ? resolveReference(left, context) : parseValue(left);
|
|
37
|
+
const rightVal = right.startsWith("$") ? resolveReference(right, context) : parseValue(right);
|
|
38
|
+
switch (op) {
|
|
39
|
+
case "===":
|
|
40
|
+
case "==":
|
|
41
|
+
return leftVal === rightVal;
|
|
42
|
+
case "!==":
|
|
43
|
+
case "!=":
|
|
44
|
+
return leftVal !== rightVal;
|
|
45
|
+
case ">":
|
|
46
|
+
return Number(leftVal) > Number(rightVal);
|
|
47
|
+
case "<":
|
|
48
|
+
return Number(leftVal) < Number(rightVal);
|
|
49
|
+
case ">=":
|
|
50
|
+
return Number(leftVal) >= Number(rightVal);
|
|
51
|
+
case "<=":
|
|
52
|
+
return Number(leftVal) <= Number(rightVal);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Truthiness check on a single reference
|
|
56
|
+
if (condition.startsWith("$")) {
|
|
57
|
+
return Boolean(resolveReference(condition, context));
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
function parseValue(raw) {
|
|
62
|
+
if (raw === "true")
|
|
63
|
+
return true;
|
|
64
|
+
if (raw === "false")
|
|
65
|
+
return false;
|
|
66
|
+
if (raw === "null")
|
|
67
|
+
return null;
|
|
68
|
+
if (raw === "undefined")
|
|
69
|
+
return undefined;
|
|
70
|
+
if (/^".*"$/.test(raw) || /^'.*'$/.test(raw))
|
|
71
|
+
return raw.slice(1, -1);
|
|
72
|
+
const num = Number(raw);
|
|
73
|
+
if (!isNaN(num))
|
|
74
|
+
return num;
|
|
75
|
+
return raw;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { UIBlockRegistry } from "../registry/ui-block-registry.js";
|
|
2
|
+
import type { UIWorkflowDefinition, UIWorkflowResult } from "../types/ui-workflow.js";
|
|
3
|
+
import type { MemoCache } from "../runtime/memoize.js";
|
|
4
|
+
export interface WorkflowEngineConfig {
|
|
5
|
+
config?: Record<string, unknown>;
|
|
6
|
+
memoCache?: MemoCache;
|
|
7
|
+
onStepStart?: (stepId: string, input: Record<string, unknown>) => void;
|
|
8
|
+
onStepEnd?: (stepId: string, output: unknown, durationMs: number) => void;
|
|
9
|
+
onStepSkip?: (stepId: string, reason: string) => void;
|
|
10
|
+
onStepError?: (stepId: string, error: unknown) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare class UIWorkflowEngine {
|
|
13
|
+
private registry;
|
|
14
|
+
private config;
|
|
15
|
+
constructor(registry: UIBlockRegistry, config?: WorkflowEngineConfig);
|
|
16
|
+
run(workflowName: string, input: Record<string, unknown>): Promise<UIWorkflowResult>;
|
|
17
|
+
execute(workflow: UIWorkflowDefinition, input: Record<string, unknown>): Promise<UIWorkflowResult>;
|
|
18
|
+
private executeStep;
|
|
19
|
+
private runErrorHandler;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=workflow-engine.d.ts.map
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { getRuntimeForBlock } from "../runtime/runtime-registry.js";
|
|
2
|
+
import { runInSandbox } from "../sandbox/sandbox.js";
|
|
3
|
+
import { deepResolve, evalCondition } from "./utils/resolve.js";
|
|
4
|
+
export class UIWorkflowEngine {
|
|
5
|
+
registry;
|
|
6
|
+
config;
|
|
7
|
+
constructor(registry, config = {}) {
|
|
8
|
+
this.registry = registry;
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async run(workflowName, input) {
|
|
12
|
+
const workflow = this.registry.getWorkflow(workflowName);
|
|
13
|
+
return this.execute(workflow, input);
|
|
14
|
+
}
|
|
15
|
+
async execute(workflow, input) {
|
|
16
|
+
const startTime = performance.now();
|
|
17
|
+
const context = {
|
|
18
|
+
input,
|
|
19
|
+
steps: {},
|
|
20
|
+
config: { ...workflow.config, ...this.config.config },
|
|
21
|
+
};
|
|
22
|
+
const stepResults = {};
|
|
23
|
+
try {
|
|
24
|
+
const steps = workflow.steps;
|
|
25
|
+
let i = 0;
|
|
26
|
+
while (i < steps.length) {
|
|
27
|
+
// Collect parallel group
|
|
28
|
+
if (steps[i].parallel) {
|
|
29
|
+
const parallelSteps = [];
|
|
30
|
+
while (i < steps.length && steps[i].parallel) {
|
|
31
|
+
parallelSteps.push(steps[i]);
|
|
32
|
+
i++;
|
|
33
|
+
}
|
|
34
|
+
const results = await Promise.all(parallelSteps.map((step) => this.executeStep(step, context, workflow)));
|
|
35
|
+
for (let j = 0; j < parallelSteps.length; j++) {
|
|
36
|
+
if (results[j] !== undefined) {
|
|
37
|
+
stepResults[parallelSteps[j].id] = { output: results[j] };
|
|
38
|
+
context.steps[parallelSteps[j].id] = { output: results[j] };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
const step = steps[i];
|
|
44
|
+
const result = await this.executeStep(step, context, workflow);
|
|
45
|
+
if (result !== undefined) {
|
|
46
|
+
stepResults[step.id] = { output: result };
|
|
47
|
+
context.steps[step.id] = { output: result };
|
|
48
|
+
}
|
|
49
|
+
i++;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
success: true,
|
|
54
|
+
steps: stepResults,
|
|
55
|
+
durationMs: performance.now() - startTime,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
// ON_ERROR handler
|
|
60
|
+
if (workflow.onError?.block) {
|
|
61
|
+
try {
|
|
62
|
+
const errorOutput = await this.runErrorHandler(workflow.onError.block, err, context);
|
|
63
|
+
return {
|
|
64
|
+
success: false,
|
|
65
|
+
steps: stepResults,
|
|
66
|
+
error: errorOutput,
|
|
67
|
+
durationMs: performance.now() - startTime,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Error handler itself failed — fall through
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
success: false,
|
|
76
|
+
steps: stepResults,
|
|
77
|
+
error: {
|
|
78
|
+
step: "unknown",
|
|
79
|
+
userMessage: err instanceof Error ? err.message : String(err),
|
|
80
|
+
code: "UNHANDLED_ERROR",
|
|
81
|
+
recoverable: false,
|
|
82
|
+
originalError: err,
|
|
83
|
+
},
|
|
84
|
+
durationMs: performance.now() - startTime,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async executeStep(step, context, workflow) {
|
|
89
|
+
// WHEN conditional
|
|
90
|
+
if (step.when) {
|
|
91
|
+
const shouldRun = evalCondition(step.when, context);
|
|
92
|
+
if (!shouldRun) {
|
|
93
|
+
this.config.onStepSkip?.(step.id, `Condition not met: ${step.when}`);
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// SUB workflow
|
|
98
|
+
if (step.sub) {
|
|
99
|
+
const resolvedInput = step.input ? deepResolve(step.input, context) : context.input;
|
|
100
|
+
this.config.onStepStart?.(step.id, resolvedInput);
|
|
101
|
+
const stepStart = performance.now();
|
|
102
|
+
const subResult = await this.run(step.sub, resolvedInput);
|
|
103
|
+
this.config.onStepEnd?.(step.id, subResult.steps, performance.now() - stepStart);
|
|
104
|
+
if (!subResult.success) {
|
|
105
|
+
throw new Error(`Sub-workflow "${step.sub}" failed: ${subResult.error?.userMessage}`);
|
|
106
|
+
}
|
|
107
|
+
return subResult.steps;
|
|
108
|
+
}
|
|
109
|
+
// Regular block execution
|
|
110
|
+
const blockRef = step.block || step.capability;
|
|
111
|
+
if (!blockRef) {
|
|
112
|
+
throw new Error(`Step "${step.id}" has no block, capability, or sub reference`);
|
|
113
|
+
}
|
|
114
|
+
const blockDef = this.registry.getBlock(blockRef);
|
|
115
|
+
const runtime = getRuntimeForBlock(blockDef.runtime);
|
|
116
|
+
const resolvedInput = step.input ? deepResolve(step.input, context) : context.input;
|
|
117
|
+
this.config.onStepStart?.(step.id, resolvedInput);
|
|
118
|
+
const stepStart = performance.now();
|
|
119
|
+
try {
|
|
120
|
+
const output = await runInSandbox(runtime, blockDef, resolvedInput, blockDef.layer, this.config.memoCache);
|
|
121
|
+
const durationMs = performance.now() - stepStart;
|
|
122
|
+
this.config.onStepEnd?.(step.id, output, durationMs);
|
|
123
|
+
return output;
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
this.config.onStepError?.(step.id, err);
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async runErrorHandler(blockRef, error, context) {
|
|
131
|
+
const blockDef = this.registry.getBlock(blockRef);
|
|
132
|
+
const runtime = getRuntimeForBlock(blockDef.runtime);
|
|
133
|
+
const errorInput = {
|
|
134
|
+
error: {
|
|
135
|
+
message: error instanceof Error ? error.message : String(error),
|
|
136
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const result = (await runtime.execute(blockDef, errorInput));
|
|
140
|
+
return {
|
|
141
|
+
step: "error-handler",
|
|
142
|
+
userMessage: String(result.userMessage ?? "An error occurred"),
|
|
143
|
+
code: String(result.code ?? "UNKNOWN_ERROR"),
|
|
144
|
+
recoverable: Boolean(result.recoverable ?? false),
|
|
145
|
+
originalError: error,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=workflow-engine.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boa-framework/ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"./react": {
|
|
13
|
+
"import": "./dist/sdk/hooks.js",
|
|
14
|
+
"types": "./dist/sdk/hooks.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc",
|
|
22
|
+
"prepublishOnly": "npm run build",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"dev": "tsc --watch"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@boa-framework/core": "*"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^5.4.0",
|
|
31
|
+
"vitest": "^1.6.0",
|
|
32
|
+
"@types/node": "^20.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|