fix build/distribution spec: npm deps not workspace, align configs with sibling projects, resolve review issues
- Replace workspace:* deps with published npm semver ranges (^0.34.49, ^0.1.0) - Expand package.json: add description, publishConfig, scripts, engines, devDependencies, conditional exports with types/default for import+require - Fix tsup entry names (path-prefixed like ujsx), add target: es2022, remove splitting:true (not used by sibling projects) - Align tsconfig with sibling projects: add lib, noUncheckedIndexedAccess, noUnusedLocals, noUnusedParameters, erasableSyntaxOnly, etc. - Expand vitest.config.ts with include, coverage, and path alias - Clarify @preact/signals-core as direct dep (not just transitive via ujsx) - Clarify @alkdev/pubsub is a consumer dependency, not flowgraph's dep - Fix edge key convention: document composite key format for call graph's multi-edge-type scenario (triggered + depends_on between same pair) - Align OperationEdgeAttrs field naming: use detail+mismatches consistently instead of compatibilityDetail - Add InvalidInputError to error hierarchy (referenced in flowgraph-api but was missing) - Fix undefined attrs.category reference in reactive-execution.md - Remove internal drafting note from host-configs.md - Fix ReactiveHostConfig constructor signature inconsistency across docs - Constrain TemplateEdgeAttrs.edgeType to sequential|conditional only
This commit is contained in:
@@ -74,59 +74,159 @@ Package structure, exports map, dependencies, and platform targets.
|
||||
|
||||
## Package JSON
|
||||
|
||||
Following the same pattern as `@alkdev/ujsx` and `@alkdev/operations` — conditional exports with explicit `types` and `default` for both import and require:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "@alkdev/flowgraph",
|
||||
"version": "0.1.0",
|
||||
"description": "Workflow graph library — DAG-based operation orchestration over graphology, with ujsx template composition and reactive execution",
|
||||
"type": "module",
|
||||
"main": "./dist/index.cjs",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/index.d.cts",
|
||||
"default": "./dist/index.cjs"
|
||||
}
|
||||
},
|
||||
"./component": {
|
||||
"import": "./dist/component/index.js",
|
||||
"require": "./dist/component/index.cjs"
|
||||
"/component": {
|
||||
"import": {
|
||||
"types": "./dist/component/index.d.ts",
|
||||
"default": "./dist/component/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/component/index.d.cts",
|
||||
"default": "./dist/component/index.cjs"
|
||||
}
|
||||
},
|
||||
"./host": {
|
||||
"import": "./dist/host/index.js",
|
||||
"require": "./dist/host/index.cjs"
|
||||
"/host": {
|
||||
"import": {
|
||||
"types": "./dist/host/index.d.ts",
|
||||
"default": "./dist/host/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/host/index.d.cts",
|
||||
"default": "./dist/host/index.cjs"
|
||||
}
|
||||
},
|
||||
"./schema": {
|
||||
"import": "./dist/schema/index.js",
|
||||
"require": "./dist/schema/index.cjs"
|
||||
"/schema": {
|
||||
"import": {
|
||||
"types": "./dist/schema/index.d.ts",
|
||||
"default": "./dist/schema/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/schema/index.d.cts",
|
||||
"default": "./dist/schema/index.cjs"
|
||||
}
|
||||
},
|
||||
"./graph": {
|
||||
"import": "./dist/graph/index.js",
|
||||
"require": "./dist/graph/index.cjs"
|
||||
"/graph": {
|
||||
"import": {
|
||||
"types": "./dist/graph/index.d.ts",
|
||||
"default": "./dist/graph/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/graph/index.d.cts",
|
||||
"default": "./dist/graph/index.cjs"
|
||||
}
|
||||
},
|
||||
"./reactive": {
|
||||
"import": "./dist/reactive/index.js",
|
||||
"require": "./dist/reactive/index.cjs"
|
||||
"/reactive": {
|
||||
"import": {
|
||||
"types": "./dist/reactive/index.d.ts",
|
||||
"default": "./dist/reactive/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/reactive/index.d.cts",
|
||||
"default": "./dist/reactive/index.cjs"
|
||||
}
|
||||
},
|
||||
"./analysis": {
|
||||
"import": "./dist/analysis/index.js",
|
||||
"require": "./dist/analysis/index.cjs"
|
||||
"/analysis": {
|
||||
"import": {
|
||||
"types": "./dist/analysis/index.d.ts",
|
||||
"default": "./dist/analysis/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/analysis/index.d.cts",
|
||||
"default": "./dist/analysis/index.cjs"
|
||||
}
|
||||
},
|
||||
"./error": {
|
||||
"import": "./dist/error/index.js",
|
||||
"require": "./dist/error/index.cjs"
|
||||
"/error": {
|
||||
"import": {
|
||||
"types": "./dist/error/index.d.ts",
|
||||
"default": "./dist/error/index.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./dist/error/index.d.cts",
|
||||
"default": "./dist/error/index.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"component": ["./dist/component/index.d.ts"],
|
||||
"host": ["./dist/host/index.d.ts"],
|
||||
"schema": ["./dist/schema/index.d.ts"],
|
||||
"graph": ["./dist/graph/index.d.ts"],
|
||||
"reactive": ["./dist/reactive/index.d.ts"],
|
||||
"analysis": ["./dist/analysis/index.d.ts"],
|
||||
"error": ["./dist/error/index.d.ts"]
|
||||
}
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"build:tsc": "tsc",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:coverage": "vitest run --coverage",
|
||||
"lint": "tsc --noEmit",
|
||||
"prepublishOnly": "npm run build"
|
||||
},
|
||||
"keywords": [
|
||||
"flowgraph",
|
||||
"dag",
|
||||
"workflow",
|
||||
"graphology",
|
||||
"ujsx",
|
||||
"operations"
|
||||
],
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@alkdev/typebox": "^0.34.49",
|
||||
"@alkdev/ujsx": "^0.1.0",
|
||||
"@preact/signals-core": "^1.14.1",
|
||||
"graphology": "^0.26.0",
|
||||
"graphology-dag": "^0.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@alkdev/operations": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"@vitest/coverage-v8": "^3.2.4",
|
||||
"tsup": "^8.5.1",
|
||||
"typescript": "^5.7.0",
|
||||
"vitest": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Exports Map: Sub-path Imports
|
||||
|
||||
The exports map uses the `"/subpath"` format (with leading slash) to match the pattern used by `@alkdev/ujsx`. This is the Node.js subpath exports convention — consumers import as:
|
||||
|
||||
```typescript
|
||||
import { FlowGraph } from "@alkdev/flowgraph/graph";
|
||||
import { typeCompat } from "@alkdev/flowgraph/analysis";
|
||||
import { h } from "@alkdev/ujsx";
|
||||
import { createRoot } from "@alkdev/ujsx/reactive";
|
||||
```
|
||||
|
||||
Each sub-path has conditional exports for both `import` (ESM) and `require` (CJS), with separate `types` files (`.d.ts` and `.d.cts`). The `typesVersions` field is NOT used — conditional exports with `types` entries replace it for modern Node.js resolution.
|
||||
|
||||
## Exports Map
|
||||
|
||||
Following the taskgraph pattern, each module has a sub-path export:
|
||||
@@ -149,14 +249,14 @@ Following the taskgraph pattern, each module has a sub-path export:
|
||||
```json
|
||||
{
|
||||
"dependencies": {
|
||||
"@alkdev/typebox": "workspace:*",
|
||||
"@alkdev/ujsx": "workspace:*",
|
||||
"@preact/signals-core": "^1.x",
|
||||
"graphology": "^0.25",
|
||||
"graphology-dag": "^0.4"
|
||||
"@alkdev/typebox": "^0.34.49",
|
||||
"@alkdev/ujsx": "^0.1.0",
|
||||
"@preact/signals-core": "^1.14.1",
|
||||
"graphology": "^0.26.0",
|
||||
"graphology-dag": "^0.4.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@alkdev/operations": "workspace:*"
|
||||
"@alkdev/operations": "^0.1.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -165,7 +265,7 @@ Following the taskgraph pattern, each module has a sub-path export:
|
||||
|---------|------|-----|
|
||||
| `@alkdev/typebox` | Schema definitions, validation, `Value.Check`, `Value.Errors` | Direct dependency — all schemas are TypeBox |
|
||||
| `@alkdev/ujsx` | UNode, HostConfig, createRoot, h(), ReactiveRoot | Direct dependency — workflow templates are ujsx trees |
|
||||
| `@preact/signals-core` | `signal`, `computed`, `effect`, `batch` | Transitive via ujsx, re-exported for flowgraph's reactive layer |
|
||||
| `@preact/signals-core` | `signal`, `computed`, `effect`, `batch` | Direct dependency — flowgraph's reactive layer uses signals directly for WorkflowReactiveRoot |
|
||||
| `graphology` | `DirectedGraph` data structure | Core graph engine — same as taskgraph |
|
||||
| `graphology-dag` | `topologicalSort`, `hasCycle`, `parallelGroups` | DAG-specific algorithms |
|
||||
| `@alkdev/operations` | `OperationSpec`, `CallEventMap`, `CallStatus` | Peer dependency — type imports only, no runtime dependency |
|
||||
@@ -178,42 +278,49 @@ Flowgraph imports `OperationSpec`, `CallEventMap`, and `CallStatus` types from `
|
||||
2. Allows flowgraph to work with any version of operations that provides the right types
|
||||
3. Reduces bundle size for consumers that don't use operations
|
||||
|
||||
### Why `@preact/signals-core` via `@alkdev/ujsx`
|
||||
### Why `@preact/signals-core` is a Direct Dependency
|
||||
|
||||
Flowgraph's reactive layer uses `signal()`, `computed()`, and `effect()` from `@preact/signals-core`. These are re-exported from `@alkdev/ujsx/reactive` so consumers don't need to import directly from Preact. If ujsx ever changes its reactive primitive library, only ujsx's re-export needs updating.
|
||||
While `@preact/signals-core` is also a dependency of `@alkdev/ujsx`, flowgraph lists it as a direct dependency because `WorkflowReactiveRoot` creates `signal()` and `computed()` instances directly. This makes the dependency explicit and ensures version alignment. Consumers import signals from flowgraph's reactive exports, not directly from Preact — the `@alkdev/ujsx/reactive` module re-exports the same signal primitives for consumers who need them, but flowgraph's own reactive module depends on `@preact/signals-core` directly for its internal signal graph construction.
|
||||
|
||||
### No `workspace:*` — Published npm Packages
|
||||
|
||||
All `@alkdev` packages are independently published to npm. The dependency versions use semver ranges (`^0.34.49`, `^0.1.0`) matching the pattern used by all sibling packages. During local development, the packages exist as local repos on disk, but they are not linked via a monorepo workspace protocol. Each package is built and published independently.
|
||||
|
||||
## Build Configuration
|
||||
|
||||
### tsup.config.ts
|
||||
|
||||
Following taskgraph's build pattern:
|
||||
Following the same pattern as `@alkdev/taskgraph` and `@alkdev/ujsx` — named entry points matching source file paths, no `splitting` flag (ujsx doesn't use it either):
|
||||
|
||||
```typescript
|
||||
import { defineConfig } from "tsup";
|
||||
import { defineConfig } from 'tsup';
|
||||
|
||||
export default defineConfig({
|
||||
entry: {
|
||||
index: "src/index.ts",
|
||||
component: "src/component/index.ts",
|
||||
host: "src/host/index.ts",
|
||||
schema: "src/schema/index.ts",
|
||||
graph: "src/graph/index.ts",
|
||||
reactive: "src/reactive/index.ts",
|
||||
analysis: "src/analysis/index.ts",
|
||||
error: "src/error/index.ts",
|
||||
index: 'src/index.ts',
|
||||
'component/index': 'src/component/index.ts',
|
||||
'host/index': 'src/host/index.ts',
|
||||
'schema/index': 'src/schema/index.ts',
|
||||
'graph/index': 'src/graph/index.ts',
|
||||
'reactive/index': 'src/reactive/index.ts',
|
||||
'analysis/index': 'src/analysis/index.ts',
|
||||
'error/index': 'src/error/index.ts',
|
||||
},
|
||||
format: ["esm", "cjs"],
|
||||
format: ['esm', 'cjs'],
|
||||
dts: true,
|
||||
clean: true,
|
||||
splitting: true,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
target: 'es2022',
|
||||
});
|
||||
```
|
||||
|
||||
- **ESM + CJS dual output** — matches all sibling packages
|
||||
- **Code splitting** — enables tree-shaking for sub-path imports
|
||||
- **Source maps** — for debugging
|
||||
- **Type declarations** — `.d.ts` files for all exports
|
||||
- **`target: 'es2022'`** — matches tsconfig target, ensures consistent output
|
||||
- **Named entry points with path prefixes** — matches ujsx pattern (e.g., `'core/schema': 'src/core/schema.ts'`), producing output like `dist/component/index.js` consistent with the exports map
|
||||
|
||||
Note: The `splitting: true` option was in an earlier draft but has been removed. The sibling projects (taskgraph, ujsx, operations) don't use splitting, and the named entry points with sub-paths already provide natural code boundaries for tree-shaking.
|
||||
|
||||
### tsconfig.json
|
||||
|
||||
@@ -221,31 +328,55 @@ export default defineConfig({
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"module": "Node16",
|
||||
"moduleResolution": "Node16",
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"types": ["vitest/globals"]
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"erasableSyntaxOnly": true
|
||||
},
|
||||
"include": ["src/**/*.ts"],
|
||||
"exclude": ["node_modules", "dist", "test"]
|
||||
}
|
||||
```
|
||||
|
||||
Matches the tsconfig pattern of all `@alkdev` packages: ES2022 target, Node16 module resolution, strict mode.
|
||||
Matches the tsconfig patterns of all `@alkdev` packages: ES2022 target, Node16 module resolution, strict mode with additional safety checks (`noUncheckedIndexedAccess`, `noUnusedLocals`, `noUnusedParameters`, `noFallthroughCasesInSwitch`, `erasableSyntaxOnly`).
|
||||
|
||||
### vitest.config.ts
|
||||
|
||||
Following the same pattern as `@alkdev/taskgraph` and `@alkdev/ujsx`:
|
||||
|
||||
```typescript
|
||||
import { defineConfig } from "vitest/config";
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import path from 'node:path';
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
include: ['test/**/*.test.ts'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.ts'],
|
||||
exclude: ['src/**/index.ts'],
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
@@ -354,8 +485,8 @@ All error paths should be tested:
|
||||
- **No network access** — no HTTP clients, WebSocket connections, or Redis clients. All data comes in through constructor arguments.
|
||||
- **`@alkdev/operations` is a peer dependency** — type imports only, no runtime dependency on the operations registry or call protocol.
|
||||
- **ESM-first** — the package is authored in ESM with CJS output generated by tsup. All internal imports use `.js` extensions for Node16 module resolution.
|
||||
- **Code splitting enabled** — tsup's `splitting: true` enables optimal code splitting for sub-path imports.
|
||||
- **Vitest for testing** — following the monorepo convention.
|
||||
- **Sub-path exports provide natural code boundaries** — named entry points in tsup match source file paths, enabling effective tree-shaking without `splitting: true`.
|
||||
- **Vitest for testing** — following the convention of all `@alkdev` packages.
|
||||
|
||||
## References
|
||||
|
||||
|
||||
Reference in New Issue
Block a user