feat(app): add composition root
This commit is contained in:
@@ -4,6 +4,7 @@ Follow these rules for the whole repository. More specific `AGENTS.md` files ove
|
||||
|
||||
## Boundaries
|
||||
- `core/`: pure domain models only. See `core/AGENTS.md`.
|
||||
- `app/`: composition root for wiring stores, command registries, input, renderer, and view adapters.
|
||||
- `commands/`: the only write path for application state.
|
||||
- `editor/`: transient editor/app state types and app store, e.g. viewport/camera and selection.
|
||||
- `input/`: keyboard, pointer, mouse, touch, pen, and wheel resolution; global consumer first, command fallback second.
|
||||
@@ -45,6 +46,7 @@ Follow these rules for the whole repository. More specific `AGENTS.md` files ove
|
||||
- Avoid ad-hoc component-local shortcuts/gestures unless they are purely local UI behavior and cannot affect app/editor/document state.
|
||||
|
||||
## Imports
|
||||
- `app/` may import from all layers; lower layers must not import from `app/`.
|
||||
- `core/` imports nothing from app layers.
|
||||
- `commands/` may import `core/` and `editor/`; avoid importing React or renderer backend APIs.
|
||||
- `editor/` may import `core/` types and command dispatch infrastructure; it must not import React, renderer, storage, or backend APIs.
|
||||
|
||||
7
app/AGENTS.md
Normal file
7
app/AGENTS.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# App Composition Rules
|
||||
|
||||
- `app/` is the composition root for runtime wiring.
|
||||
- Create registries, stores, command sets, input adapters, and renderer/view adapters here.
|
||||
- Do not implement domain rules, command behavior, rendering backend logic, or React UI here.
|
||||
- App composition may import from all layers, but lower layers must not import from `app/`.
|
||||
- Keep factories testable; avoid module-level mutable singleton state unless explicitly required by the runtime entrypoint.
|
||||
16
app/app.ts
Normal file
16
app/app.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { createCommandRegistry } from "@commands/registry";
|
||||
import { viewportCommands } from "@commands/viewport";
|
||||
import { createInitialAppState } from "@editor/initial-state";
|
||||
import { createAppStore } from "@editor/store";
|
||||
|
||||
export type ImageStudioApp = ReturnType<typeof createImageStudioApp>;
|
||||
|
||||
export function createImageStudioApp(options?: { documentName?: string }) {
|
||||
const registry = createCommandRegistry([...viewportCommands]);
|
||||
const store = createAppStore(createInitialAppState(options?.documentName), registry);
|
||||
|
||||
return {
|
||||
registry,
|
||||
store,
|
||||
};
|
||||
}
|
||||
2
app/index.ts
Normal file
2
app/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export type { ImageStudioApp } from "./app";
|
||||
export { createImageStudioApp } from "./app";
|
||||
@@ -2,11 +2,13 @@ import js from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
const appLayerImports = [
|
||||
"@app/*",
|
||||
"@view/*",
|
||||
"@renderer/*",
|
||||
"@commands/*",
|
||||
"@editor/*",
|
||||
"@input/*",
|
||||
"../app/*",
|
||||
"../view/*",
|
||||
"../renderer/*",
|
||||
"../commands/*",
|
||||
@@ -30,12 +32,7 @@ export default tseslint.config(
|
||||
{
|
||||
files: ["core/**/*.{ts,tsx}"],
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: appLayerImports,
|
||||
},
|
||||
],
|
||||
"no-restricted-imports": ["error", { patterns: appLayerImports }],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -43,9 +40,7 @@ export default tseslint.config(
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: ["@view/*", "@renderer/*", "../view/*", "../renderer/*", "react", "react-dom"],
|
||||
},
|
||||
{ patterns: ["@app/*", "@view/*", "@renderer/*", "../app/*", "../view/*", "../renderer/*", "react", "react-dom"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -54,9 +49,7 @@ export default tseslint.config(
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: ["@view/*", "@renderer/*", "../view/*", "../renderer/*", "react", "react-dom"],
|
||||
},
|
||||
{ patterns: ["@app/*", "@view/*", "@renderer/*", "../app/*", "../view/*", "../renderer/*", "react", "react-dom"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -65,9 +58,7 @@ export default tseslint.config(
|
||||
rules: {
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: ["@view/*", "../view/*", "react", "react-dom"],
|
||||
},
|
||||
{ patterns: ["@app/*", "@view/*", "../app/*", "../view/*", "react", "react-dom"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -77,7 +68,18 @@ export default tseslint.config(
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
patterns: ["@view/*", "@renderer/*", "@editor/*", "../view/*", "../renderer/*", "../editor/*", "react", "react-dom"],
|
||||
patterns: [
|
||||
"@app/*",
|
||||
"@view/*",
|
||||
"@renderer/*",
|
||||
"@editor/*",
|
||||
"../app/*",
|
||||
"../view/*",
|
||||
"../renderer/*",
|
||||
"../editor/*",
|
||||
"react",
|
||||
"react-dom",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
"@renderer/*": ["./renderer/*"],
|
||||
"@commands/*": ["./commands/*"],
|
||||
"@editor/*": ["./editor/*"],
|
||||
"@input/*": ["./input/*"]
|
||||
"@input/*": ["./input/*"],
|
||||
"@app/*": ["./app/*"]
|
||||
},
|
||||
|
||||
// Some stricter flags (disabled by default)
|
||||
|
||||
Reference in New Issue
Block a user