diff --git a/docs/ja-JP/skills/tinystruct-patterns/SKILL.md b/docs/ja-JP/skills/tinystruct-patterns/SKILL.md index 4f46ea6b0..32d5c98c7 100644 --- a/docs/ja-JP/skills/tinystruct-patterns/SKILL.md +++ b/docs/ja-JP/skills/tinystruct-patterns/SKILL.md @@ -3,7 +3,7 @@ name: tinystruct-patterns description: tinystruct Java フレームワークで開発する際の専門ガイダンス。tinystruct コードベース、または tinystruct 上に構築されたあらゆるプロジェクトで作業する際に使用します — プロジェクトに存在しない場合の bin/dispatcher・bin/dispatcher.cmd 起動スクリプトの生成、Application クラスの作成、@Action によるルート定義、ユニットテスト、ActionRegistry、HTTP/CLI デュアルモード対応、組み込み HTTP サーバー、イベントシステム、Builder/Builders による JSON 処理、AbstractData によるデータベース永続化、POJO 生成、Server-Sent Events (SSE)、ファイルアップロード、アウトバウンド HTTP 通信を含みます。 metadata: origin: ECC - upstream: tinystruct==1.7.34 + upstream: tinystruct==1.7.37 --- # tinystruct 開発パターン @@ -12,11 +12,11 @@ metadata: ## コア原則 -**CLI と HTTP は等しい市民(equal citizens)です。** `@Action` でアノテーションされたすべてのメソッドは、理想的には変更なしでターミナルとWebブラウザの両方から実行可能であるべきです。この「デュアルモード」対応能力が tinystruct のコア設計思想です。 +**CLI と HTTP は等しい市民(equal citizens)です。** トランスポート中立な `@Action`(`mode` を指定しないもの、つまり `Mode.DEFAULT`)は、変更なしでターミナルとWebブラウザの両方から実行できます。この「デュアルモード」対応能力が tinystruct のコア設計思想です。トランスポート固有のモード(`Mode.CLI`、`Mode.HTTP_GET`、`Mode.HTTP_POST` など)を持つアクションは意図的にそのトランスポートに限定されており、もう一方から呼び出すとフレームワークは "The action is not allowed to be executed." で拒否します。 ## 主要な開発ツール:`bin/dispatcher` -**`bin/dispatcher` は tinystruct アプリケーションを開発・実行・テスト・デバッグするためのデフォルトかつ最優先のツールです — IDE の実行構成、手書きの `main()`、curl、ブラウザよりも先にこれを使ってください。** すべての `@Action` は設計上デュアルモードであるため、`bin/dispatcher` を使うことでサーバーやブラウザを必要とせず、ターミナルから直接ルーティング・引数バインディング・ビジネスロジックを、最速のフィードバックループで検証できます。 +**`bin/dispatcher` は tinystruct アプリケーションを開発・実行・テスト・デバッグするためのデフォルトかつ最優先のツールです — IDE の実行構成、手書きの `main()`、curl、ブラウザよりも先にこれを使ってください。** トランスポート中立な `@Action` メソッドは設計上デュアルモードであるため、`bin/dispatcher` を使うことでサーバーやブラウザを必要とせず、ターミナルから直接ルーティング・引数バインディング・ビジネスロジックを、最速のフィードバックループで検証できます。HTTPメソッドに限定されたアクション(`Mode.HTTP_POST`、`Mode.HTTP_GET` など)は `bin/dispatcher` からは呼び出せないため、起動中のサーバーに対してHTTPクライアントでテストしてください。 ```bash # @Action を直接実行 - 新しいアクションが動作するか確認する最速の方法 @@ -36,7 +36,7 @@ bin/dispatcher --help bin/dispatcher --version ``` -このワークフローをデフォルトにしてください:`@Action` を実装したら、`bin/dispatcher ` ですぐに実行して CLI モードで正しく動作することを確認し、Web向けのパス(例:`mode = Mode.HTTP_POST`、セッション、ファイルアップロードなど)を検証する必要があるときだけ HTTP サーバー(これも `bin/dispatcher start ...` で)を起動します。アプリのエントリポイントとして `main(String[] args)` を決してハードコードしないでください — `bin/dispatcher`(Windows では `bin/dispatcher.cmd`)がすべてのモジュールの唯一のエントリポイントです。 +このワークフローをデフォルトにしてください:トランスポート中立な `@Action` を実装したら、`bin/dispatcher ` ですぐに実行して CLI モードで正しく動作することを確認し、Web向けのパス(例:`mode = Mode.HTTP_POST`、セッション、ファイルアップロードなど)を検証する必要があるときだけ HTTP サーバー(これも `bin/dispatcher start ...` で)を起動します。HTTP専用のアクションではCLIの手順を省略してHTTPでテストし、`Mode.CLI` のアクションではサーバーは不要です。アプリのエントリポイントとして `main(String[] args)` を決してハードコードしないでください — `bin/dispatcher`(Windows では `bin/dispatcher.cmd`)がすべてのモジュールの唯一のエントリポイントです。 ### `bin/dispatcher` と `bin/dispatcher.cmd` の生成 @@ -139,7 +139,17 @@ public class MyService extends AbstractApplication { ```java @Action(value = "login", mode = Mode.HTTP_POST) public String doLogin(Request request) throws ApplicationException { - request.getSession().setAttribute("userId", "42"); + String username = request.getParameter("username"); + String password = request.getParameter("password"); + + // AuthService はアプリケーション独自のクラスであり、tinystruct の一部ではない + String userId = AuthService.getInstance().authenticate(username, password); // 失敗時は null + if (userId == null) { + throw new ApplicationException("Invalid credentials"); + } + + // 認証に成功した後にのみセッションを操作する + request.getSession().setAttribute("userId", userId); return "Logged in"; } ``` @@ -223,7 +233,7 @@ MCP API(例:`org.tinystruct.mcp.MCPTool`、`org.tinystruct.mcp.MCPServer`、 org.tinystruct tinystruct - 1.7.34 + 1.7.37 ``` @@ -312,6 +322,9 @@ database.password= default.home.page=hello server.port=8080 default.server.open_browser=true +# 静的ファイルはこれらのディレクトリ(カンマまたはセミコロン区切り)からのみ配信されます。 +# ディレクトリは作業ディレクトリのサブディレクトリである必要があります。デフォルト:static +# server.static.dirs=static,public # ロケール default.language=en_US @@ -325,6 +338,13 @@ default.language=en_US logging.enabled=true logging.level=INFO org.tinystruct.level=FINE +# 作成するハンドラ(カンマまたは空白区切り)。デフォルト:ConsoleHandler のみ +handlers=java.util.logging.ConsoleHandler +# ローテーション付きのログファイルも書き出すには、FileHandler を追加する: +# handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler +# java.util.logging.FileHandler.pattern=logs/tinystruct-%g.log +# java.util.logging.FileHandler.limit=10485760 +# java.util.logging.FileHandler.count=5 ``` アプリケーション内で設定値にアクセスする: @@ -339,6 +359,11 @@ String port = this.getConfiguration("server.port"); - **ANSI コンソールカラー**:コンソール出力はログレベルに応じて色分けされます(SEVERE は赤、WARNING は黄、INFO は緑、CONFIG はシアン、FINE/デバッグログはグレー)。 - **正確な呼び出し元トレース**:Java の `StackWalker` API を使用して実行時のコールスタックをトレースします。ログを発生させた呼び出し元の正確なクラス、メソッド、ファイル名、行番号を特定してログに記録します(内部のユーティリティ層やロギング層は経由しません)。 - **パッケージ/ロガーレベルの上書き**:`application.properties` にパッケージ単位の上書きを直接設定できます(例:`org.tinystruct.level=FINE`)。 +- **設定可能なハンドラ**:`handlers` プロパティで作成するハンドラを列挙します。デフォルトは `java.util.logging.ConsoleHandler` です。各ハンドラは自身の `.level`、`.formatter`、`.encoding` を読み取ります。`java.util.logging.FileHandler` はオプトインで、`java.util.logging.FileHandler.pattern` が必要です。`limit`(デフォルト `0`、無制限)、`count`(デフォルト `1`)、`append`(デフォルト `true`)でローテーションを制御します。ANSI カラーはコンソール出力にのみ適用され、ログファイルには適用されません。 + +## 静的ファイル + +組み込みの `HttpServer` は、`server.static.dirs`(カンマまたはセミコロン区切り)、それがなければ `server.static.dir` で指定されたディレクトリからのみ静的ファイルを配信します。デフォルトは `static` です。各ディレクトリは作業ディレクトリのサブディレクトリである必要があり、そうでない場合、そのディレクトリの静的配信は無効になり警告がログに記録されます。`.` や `..` のセグメントを含むリクエスト、隠しファイルやフォルダ(`.git` や `.env` など `.` で始まるもの)を指すリクエスト、`.` や空白で終わる名前、プロジェクトルート直下のファイルを指すリクエストは拒否されます。プロジェクトルートが配信されることを期待せず、公開アセットは `static/` 配下に置いてください。 ## レッドフラグとアンチパターン @@ -357,6 +382,9 @@ String port = this.getConfiguration("server.port"); | 実行時にアクションが見つからない | クラスが `--import` でインポートされているか、`application.properties` に列挙されているか確認する。 | | CLI引数が認識されない | `--key value` の形で渡し、`getContext().getAttribute("--key")` でアクセスする。 | | 同じパスの2つのメソッドで、意図しない方が呼ばれる | 明示的な `mode`(例:`HTTP_GET` と `HTTP_POST`)を設定して区別する。 | +| `bin/dispatcher` が "The action is not allowed to be executed." で失敗する | そのアクションはHTTP固有のモードを持っており、CLIからは呼び出せない。HTTPでテストするか、デュアルモードにしたい場合は `mode` を外す。 | +| ファイルが存在するのに静的ファイルが404を返す | `server.static.dirs` / `server.static.dir`(デフォルト `static/`)のみが配信される。隠しファイル、`..` を含むパス、プロジェクトルート直下のファイルは常に拒否される。 | +| ログファイルが書き出されない | `FileHandler` はオプトイン:`handlers` に `java.util.logging.FileHandler` を追加し、`java.util.logging.FileHandler.pattern` を設定する。 | ## ベストプラクティス diff --git a/skills/tinystruct-patterns/SKILL.md b/skills/tinystruct-patterns/SKILL.md index f354debc0..60f8947cd 100644 --- a/skills/tinystruct-patterns/SKILL.md +++ b/skills/tinystruct-patterns/SKILL.md @@ -3,7 +3,7 @@ name: tinystruct-patterns description: Expert guidance for developing with the tinystruct Java framework. Use when working on the tinystruct codebase or any project built on tinystruct — including generating the bin/dispatcher and bin/dispatcher.cmd launcher scripts when a project lacks them, creating Application classes, @Action-mapped routes, unit tests, ActionRegistry, HTTP/CLI dual-mode handling, the built-in HTTP server, the event system, JSON with Builder/Builders, database persistence with AbstractData, POJO generation, Server-Sent Events (SSE), file uploads, and outbound HTTP networking. metadata: origin: ECC - upstream: tinystruct==1.7.34 + upstream: tinystruct==1.7.37 --- # tinystruct Development Patterns @@ -12,11 +12,11 @@ Architecture and implementation patterns for building modules with the **tinystr ## Core Principle -**CLI and HTTP are equal citizens.** Every method annotated with `@Action` should ideally be runnable from both a terminal and a web browser without modification. This "dual-mode" capability is the core design philosophy of tinystruct. +**CLI and HTTP are equal citizens.** Every transport-neutral `@Action` (one with no `mode`, i.e. `Mode.DEFAULT`) can be run from both a terminal and a web browser without modification. This "dual-mode" capability is the core design philosophy of tinystruct. An action with a transport-specific mode (`Mode.CLI`, `Mode.HTTP_GET`, `Mode.HTTP_POST`, ...) is deliberately bound to that transport: the framework rejects it from the other one with "The action is not allowed to be executed." ## Primary Development Tool: `bin/dispatcher` -**`bin/dispatcher` is the default, highest-priority tool for developing, running, testing, and debugging a tinystruct application — reach for it before an IDE run configuration, a hand-written `main()`, curl, or a browser.** Because every `@Action` is dual-mode by design, `bin/dispatcher` lets you exercise routing, argument binding, and business logic directly from the terminal, with the fastest possible feedback loop and no server/browser required. +**`bin/dispatcher` is the default, highest-priority tool for developing, running, testing, and debugging a tinystruct application — reach for it before an IDE run configuration, a hand-written `main()`, curl, or a browser.** Because transport-neutral `@Action` methods are dual-mode by design, `bin/dispatcher` lets you exercise routing, argument binding, and business logic directly from the terminal, with the fastest possible feedback loop and no server/browser required. Actions bound to an HTTP method (`Mode.HTTP_POST`, `Mode.HTTP_GET`, ...) cannot be reached from `bin/dispatcher`; test those through an HTTP client against the running server. ```bash # Run any @Action directly - the fastest way to verify a new action works @@ -36,7 +36,7 @@ bin/dispatcher --help bin/dispatcher --version ``` -Default to this workflow: implement the `@Action`, run it immediately via `bin/dispatcher ` to confirm it behaves correctly in CLI mode, and only start the HTTP server (also via `bin/dispatcher start ...`) once you need to verify the web-facing path (e.g. `mode = Mode.HTTP_POST`, sessions, file uploads). Never hardcode a `main(String[] args)` as an app's entry point — `bin/dispatcher` (or `bin/dispatcher.cmd` on Windows) is the one entry point for every module. +Default to this workflow: implement a transport-neutral `@Action`, run it immediately via `bin/dispatcher ` to confirm it behaves correctly in CLI mode, and only start the HTTP server (also via `bin/dispatcher start ...`) once you need to verify the web-facing path (e.g. `mode = Mode.HTTP_POST`, sessions, file uploads). For HTTP-only actions skip the CLI step and test them over HTTP; for `Mode.CLI` actions no server is needed. Never hardcode a `main(String[] args)` as an app's entry point — `bin/dispatcher` (or `bin/dispatcher.cmd` on Windows) is the one entry point for every module. ### Generating `bin/dispatcher` and `bin/dispatcher.cmd` @@ -139,7 +139,17 @@ public class MyService extends AbstractApplication { ```java @Action(value = "login", mode = Mode.HTTP_POST) public String doLogin(Request request) throws ApplicationException { - request.getSession().setAttribute("userId", "42"); + String username = request.getParameter("username"); + String password = request.getParameter("password"); + + // AuthService is your application's own class, not part of tinystruct + String userId = AuthService.getInstance().authenticate(username, password); // null on failure + if (userId == null) { + throw new ApplicationException("Invalid credentials"); + } + + // Only touch the session after a successful authentication + request.getSession().setAttribute("userId", userId); return "Logged in"; } ``` @@ -224,7 +234,7 @@ The MCP APIs (e.g., `org.tinystruct.mcp.MCPTool`, `org.tinystruct.mcp.MCPServer` org.tinystruct tinystruct - 1.7.34 + 1.7.37 ``` @@ -313,6 +323,9 @@ database.password= default.home.page=hello server.port=8080 default.server.open_browser=true +# Static files are served only from these directories (comma- or semicolon-separated), +# which must be sub-directories of the working directory. Default: static +# server.static.dirs=static,public # Locale default.language=en_US @@ -326,6 +339,13 @@ default.language=en_US logging.enabled=true logging.level=INFO org.tinystruct.level=FINE +# Handlers to create (comma- or whitespace-separated). Default: ConsoleHandler only +handlers=java.util.logging.ConsoleHandler +# To also write rotating log files, append the FileHandler: +# handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler +# java.util.logging.FileHandler.pattern=logs/tinystruct-%g.log +# java.util.logging.FileHandler.limit=10485760 +# java.util.logging.FileHandler.count=5 ``` Access config values in your application: @@ -340,6 +360,11 @@ The framework includes a programmatic wrapper around `java.util.logging` (JUL) t - **ANSI Console Colors**: Console output is color-coded based on the log level (red for SEVERE, yellow for WARNING, green for INFO, cyan for CONFIG, and grey for FINE/debug logs). - **Precise Caller Tracing**: Uses Java's `StackWalker` API to trace the call stack at runtime. It identifies and logs the exact class, method, filename, and line number of the caller that initiated the log (bypassing internal utility and logging layers). - **Package/Logger Level Overrides**: Set package-specific overrides directly in `application.properties` (e.g., `org.tinystruct.level=FINE`). +- **Configurable Handlers**: The `handlers` property lists the handlers to create; it defaults to `java.util.logging.ConsoleHandler`. Each handler reads its own `.level`, `.formatter` and `.encoding`. `java.util.logging.FileHandler` is opt-in and needs `java.util.logging.FileHandler.pattern`; `limit` (default `0`, unlimited), `count` (default `1`) and `append` (default `true`) control rotation. ANSI colors are applied to console output only, never to log files. + +## Static Files + +The built-in `HttpServer` serves static files only from the directories named by `server.static.dirs` (comma- or semicolon-separated) or, failing that, `server.static.dir`; the default is `static`. Each directory must be a sub-directory of the working directory, otherwise static serving is disabled for it and a warning is logged. Requests are rejected when they contain `.` or `..` segments, name hidden files or folders (anything starting with `.`, such as `.git` or `.env`), end in `.` or a space, or point at files in the project root. Put public assets under `static/` rather than expecting the project root to be served. ## Red Flags & Anti-patterns @@ -358,6 +383,9 @@ The framework includes a programmatic wrapper around `java.util.logging` (JUL) t | Action not found at runtime | Ensure class is imported via `--import` or listed in `application.properties`. | | CLI arg not visible | Pass with `--key value`; access via `getContext().getAttribute("--key")`. | | Two methods same path, wrong one fires | Set explicit `mode` (e.g., `HTTP_GET` vs `HTTP_POST`) to disambiguate. | +| `bin/dispatcher` fails with "The action is not allowed to be executed." | The action has an HTTP-specific mode; it is not reachable from the CLI. Test it over HTTP, or drop `mode` if it should be dual-mode. | +| A static file returns 404 though it exists | Only `server.static.dirs` / `server.static.dir` (default `static/`) are served; hidden files, `..` paths and project-root files are always refused. | +| No log file is written | `FileHandler` is opt-in: add `java.util.logging.FileHandler` to `handlers` and set `java.util.logging.FileHandler.pattern`. | ## Best Practices