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
|
## Boundaries
|
||||||
- `core/`: pure domain models only. See `core/AGENTS.md`.
|
- `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.
|
- `commands/`: the only write path for application state.
|
||||||
- `editor/`: transient editor/app state types and app store, e.g. viewport/camera and selection.
|
- `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.
|
- `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.
|
- Avoid ad-hoc component-local shortcuts/gestures unless they are purely local UI behavior and cannot affect app/editor/document state.
|
||||||
|
|
||||||
## Imports
|
## Imports
|
||||||
|
- `app/` may import from all layers; lower layers must not import from `app/`.
|
||||||
- `core/` imports nothing from app layers.
|
- `core/` imports nothing from app layers.
|
||||||
- `commands/` may import `core/` and `editor/`; avoid importing React or renderer backend APIs.
|
- `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.
|
- `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";
|
import tseslint from "typescript-eslint";
|
||||||
|
|
||||||
const appLayerImports = [
|
const appLayerImports = [
|
||||||
|
"@app/*",
|
||||||
"@view/*",
|
"@view/*",
|
||||||
"@renderer/*",
|
"@renderer/*",
|
||||||
"@commands/*",
|
"@commands/*",
|
||||||
"@editor/*",
|
"@editor/*",
|
||||||
"@input/*",
|
"@input/*",
|
||||||
|
"../app/*",
|
||||||
"../view/*",
|
"../view/*",
|
||||||
"../renderer/*",
|
"../renderer/*",
|
||||||
"../commands/*",
|
"../commands/*",
|
||||||
@@ -30,12 +32,7 @@ export default tseslint.config(
|
|||||||
{
|
{
|
||||||
files: ["core/**/*.{ts,tsx}"],
|
files: ["core/**/*.{ts,tsx}"],
|
||||||
rules: {
|
rules: {
|
||||||
"no-restricted-imports": [
|
"no-restricted-imports": ["error", { patterns: appLayerImports }],
|
||||||
"error",
|
|
||||||
{
|
|
||||||
patterns: appLayerImports,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -43,9 +40,7 @@ export default tseslint.config(
|
|||||||
rules: {
|
rules: {
|
||||||
"no-restricted-imports": [
|
"no-restricted-imports": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{ patterns: ["@app/*", "@view/*", "@renderer/*", "../app/*", "../view/*", "../renderer/*", "react", "react-dom"] },
|
||||||
patterns: ["@view/*", "@renderer/*", "../view/*", "../renderer/*", "react", "react-dom"],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -54,9 +49,7 @@ export default tseslint.config(
|
|||||||
rules: {
|
rules: {
|
||||||
"no-restricted-imports": [
|
"no-restricted-imports": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{ patterns: ["@app/*", "@view/*", "@renderer/*", "../app/*", "../view/*", "../renderer/*", "react", "react-dom"] },
|
||||||
patterns: ["@view/*", "@renderer/*", "../view/*", "../renderer/*", "react", "react-dom"],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -65,9 +58,7 @@ export default tseslint.config(
|
|||||||
rules: {
|
rules: {
|
||||||
"no-restricted-imports": [
|
"no-restricted-imports": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{ patterns: ["@app/*", "@view/*", "../app/*", "../view/*", "react", "react-dom"] },
|
||||||
patterns: ["@view/*", "../view/*", "react", "react-dom"],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -77,7 +68,18 @@ export default tseslint.config(
|
|||||||
"no-restricted-imports": [
|
"no-restricted-imports": [
|
||||||
"error",
|
"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/*"],
|
"@renderer/*": ["./renderer/*"],
|
||||||
"@commands/*": ["./commands/*"],
|
"@commands/*": ["./commands/*"],
|
||||||
"@editor/*": ["./editor/*"],
|
"@editor/*": ["./editor/*"],
|
||||||
"@input/*": ["./input/*"]
|
"@input/*": ["./input/*"],
|
||||||
|
"@app/*": ["./app/*"]
|
||||||
},
|
},
|
||||||
|
|
||||||
// Some stricter flags (disabled by default)
|
// Some stricter flags (disabled by default)
|
||||||
|
|||||||
Reference in New Issue
Block a user