@oyolincc/create 0.0.0-dev-20260513110237 → 0.0.0-dev-20260918103756
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.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +35 -17
- package/templates/hono-fullstack/.agents/skills/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/templates/hono-fullstack/.agents/skills/ask-matt/SKILL.md +90 -0
- package/templates/hono-fullstack/.agents/skills/ask-matt/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/code-review/SKILL.md +87 -0
- package/templates/hono-fullstack/.agents/skills/code-review/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/codebase-design/DEEPENING.md +37 -0
- package/templates/hono-fullstack/.agents/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/templates/hono-fullstack/.agents/skills/codebase-design/SKILL.md +114 -0
- package/templates/hono-fullstack/.agents/skills/codebase-design/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/confirm-requirements/SKILL.md +25 -0
- package/templates/hono-fullstack/.agents/skills/diagnosing-bugs/SKILL.md +140 -0
- package/templates/hono-fullstack/.agents/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/templates/hono-fullstack/.agents/skills/domain-modeling/ADR-FORMAT.md +47 -0
- package/templates/hono-fullstack/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/templates/hono-fullstack/.agents/skills/domain-modeling/SKILL.md +74 -0
- package/templates/hono-fullstack/.agents/skills/domain-modeling/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/grill-me/SKILL.md +7 -0
- package/templates/hono-fullstack/.agents/skills/grill-me/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/grilling/SKILL.md +22 -0
- package/templates/hono-fullstack/.agents/skills/grilling/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/implement/SKILL.md +15 -0
- package/templates/hono-fullstack/.agents/skills/implement/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/templates/hono-fullstack/.agents/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/templates/hono-fullstack/.agents/skills/improve-codebase-architecture/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/prototype/LOGIC.md +67 -0
- package/templates/hono-fullstack/.agents/skills/prototype/SKILL.md +26 -0
- package/templates/hono-fullstack/.agents/skills/prototype/UI.md +112 -0
- package/templates/hono-fullstack/.agents/skills/prototype/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/research/SKILL.md +12 -0
- package/templates/hono-fullstack/.agents/skills/research/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/resolving-merge-conflicts/SKILL.md +14 -0
- package/templates/hono-fullstack/.agents/skills/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/scoped-code-review/SKILL.md +113 -0
- package/templates/hono-fullstack/.agents/skills/setup-matt-pocock-skills/SKILL.md +116 -0
- package/templates/hono-fullstack/.agents/skills/setup-matt-pocock-skills/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/setup-matt-pocock-skills/domain.md +51 -0
- package/templates/hono-fullstack/.agents/skills/setup-matt-pocock-skills/issue-tracker-github.md +45 -0
- package/templates/hono-fullstack/.agents/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +46 -0
- package/templates/hono-fullstack/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +30 -0
- package/templates/hono-fullstack/.agents/skills/setup-matt-pocock-skills/triage-labels.md +15 -0
- package/templates/hono-fullstack/.agents/skills/tdd/SKILL.md +38 -0
- package/templates/hono-fullstack/.agents/skills/tdd/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/tdd/mocking.md +59 -0
- package/templates/hono-fullstack/.agents/skills/tdd/tests.md +77 -0
- package/templates/hono-fullstack/.agents/skills/teach/GLOSSARY-FORMAT.md +35 -0
- package/templates/hono-fullstack/.agents/skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/templates/hono-fullstack/.agents/skills/teach/MISSION-FORMAT.md +31 -0
- package/templates/hono-fullstack/.agents/skills/teach/RESOURCES-FORMAT.md +32 -0
- package/templates/hono-fullstack/.agents/skills/teach/SKILL.md +140 -0
- package/templates/hono-fullstack/.agents/skills/teach/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/to-questionnaire/SKILL.md +53 -0
- package/templates/hono-fullstack/.agents/skills/to-questionnaire/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/triage/AGENT-BRIEF.md +207 -0
- package/templates/hono-fullstack/.agents/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/templates/hono-fullstack/.agents/skills/triage/SKILL.md +112 -0
- package/templates/hono-fullstack/.agents/skills/triage/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/wait-what/SKILL.md +7 -0
- package/templates/hono-fullstack/.agents/skills/wait-what/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/wayfinder/SKILL.md +128 -0
- package/templates/hono-fullstack/.agents/skills/wayfinder/agents/openai.yaml +5 -0
- package/templates/hono-fullstack/.agents/skills/wizard/SKILL.md +44 -0
- package/templates/hono-fullstack/.agents/skills/wizard/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.agents/skills/wizard/template.sh +204 -0
- package/templates/hono-fullstack/.agents/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/templates/hono-fullstack/.agents/skills/writing-for-agents/SKILL.md +81 -0
- package/templates/hono-fullstack/.agents/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/templates/hono-fullstack/.changeset/README.md +8 -0
- package/templates/hono-fullstack/.changeset/config.json +11 -0
- package/templates/hono-fullstack/.vscode/extensions.json +5 -0
- package/templates/hono-fullstack/.vscode/launch.json +25 -0
- package/templates/hono-fullstack/.vscode/settings.json +24 -0
- package/templates/hono-fullstack/AGENTS.md +43 -0
- package/templates/hono-fullstack/README.md +37 -0
- package/templates/hono-fullstack/_gitignore +30 -0
- package/templates/hono-fullstack/apps/server/package.json +56 -0
- package/templates/hono-fullstack/apps/server/src/adapters/nodejs/bootstrap.ts +64 -0
- package/templates/hono-fullstack/apps/server/src/adapters/nodejs/config.ts +53 -0
- package/templates/hono-fullstack/apps/server/src/adapters/nodejs/entry.ts +47 -0
- package/templates/hono-fullstack/apps/server/src/app.test.ts +52 -0
- package/templates/hono-fullstack/apps/server/src/app.ts +45 -0
- package/templates/hono-fullstack/apps/server/src/infra/logger/getter.ts +74 -0
- package/templates/hono-fullstack/apps/server/src/infra/logger/pino.ts +122 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/constants.ts +23 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/errors.ts +40 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/example/runner.test.ts +56 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/example/runner.ts +185 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/example/worker-entry.ts +34 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/types.ts +113 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/utils/business-error.ts +19 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/utils/message.ts +46 -0
- package/templates/hono-fullstack/apps/server/src/infra/work-runner/utils/transfer.ts +144 -0
- package/templates/hono-fullstack/apps/server/src/middlewares/cors.ts +26 -0
- package/templates/hono-fullstack/apps/server/src/modules/example/contracts.ts +9 -0
- package/templates/hono-fullstack/apps/server/src/modules/example/routes.ts +75 -0
- package/templates/hono-fullstack/apps/server/src/modules/example/work-task/tasks.test.ts +37 -0
- package/templates/hono-fullstack/apps/server/src/modules/example/work-task/tasks.ts +35 -0
- package/templates/hono-fullstack/apps/server/src/shared/app/types.ts +40 -0
- package/templates/hono-fullstack/apps/server/src/shared/errors/constants.ts +152 -0
- package/templates/hono-fullstack/apps/server/src/shared/errors/defs/index.ts +15 -0
- package/templates/hono-fullstack/apps/server/src/shared/errors/handler.ts +16 -0
- package/templates/hono-fullstack/apps/server/src/shared/errors/log.ts +67 -0
- package/templates/hono-fullstack/apps/server/src/shared/errors/proto.ts +42 -0
- package/templates/hono-fullstack/apps/server/src/shared/errors/types.ts +29 -0
- package/templates/hono-fullstack/apps/server/src/shared/network/fetcher.ts +35 -0
- package/templates/hono-fullstack/apps/server/src/shared/network/openapi/index.ts +50 -0
- package/templates/hono-fullstack/apps/server/src/shared/network/openapi/res-defs.ts +39 -0
- package/templates/hono-fullstack/apps/server/src/shared/network/types.ts +3 -0
- package/templates/hono-fullstack/apps/server/src/shared/network/utils.ts +36 -0
- package/templates/hono-fullstack/apps/server/src/shared/network/validator.ts +21 -0
- package/templates/hono-fullstack/apps/server/src/shared/utils/index.ts +30 -0
- package/templates/hono-fullstack/apps/server/src/virtual-modules.d.ts +5 -0
- package/templates/hono-fullstack/apps/server/tsconfig.json +16 -0
- package/templates/hono-fullstack/apps/server/vite.config.ts +79 -0
- package/templates/hono-fullstack/apps/stack/frontend/modules/example/ExamplePage.tsx +57 -0
- package/templates/hono-fullstack/apps/stack/frontend/modules/example/api.ts +13 -0
- package/templates/hono-fullstack/apps/stack/frontend/modules/example/hooks.ts +13 -0
- package/templates/hono-fullstack/apps/stack/frontend/routeTree.gen.ts +56 -0
- package/templates/hono-fullstack/apps/stack/frontend/router.tsx +22 -0
- package/templates/hono-fullstack/apps/stack/frontend/routes/__root.tsx +61 -0
- package/templates/hono-fullstack/apps/stack/frontend/routes/index.tsx +4 -0
- package/templates/hono-fullstack/apps/stack/frontend/shared/network/client.ts +19 -0
- package/templates/hono-fullstack/apps/stack/frontend/shared/network/constants.ts +1 -0
- package/templates/hono-fullstack/apps/stack/frontend/shared/network/errors.ts +33 -0
- package/templates/hono-fullstack/apps/stack/frontend/styles.css +23 -0
- package/templates/hono-fullstack/apps/stack/frontend/vite-env.d.ts +1 -0
- package/templates/hono-fullstack/apps/stack/nitro/handlers/hono.ts +1 -0
- package/templates/hono-fullstack/apps/stack/nitro/plugins/hono.ts +9 -0
- package/templates/hono-fullstack/apps/stack/package.json +43 -0
- package/templates/hono-fullstack/apps/stack/tsconfig.json +10 -0
- package/templates/hono-fullstack/apps/stack/tsr.config.json +5 -0
- package/templates/hono-fullstack/apps/stack/vite.config.ts +33 -0
- package/templates/hono-fullstack/docs/agents/domain.md +41 -0
- package/templates/hono-fullstack/docs/agents/issue-tracker.md +30 -0
- package/templates/hono-fullstack/docs/agents/triage-labels.md +15 -0
- package/templates/hono-fullstack/envs/.env.example.server +12 -0
- package/templates/hono-fullstack/envs/.env.example.sqlite +2 -0
- package/templates/hono-fullstack/libs/db/drizzle-kit/sqlite.config.ts +22 -0
- package/templates/hono-fullstack/libs/db/package.json +39 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/client.ts +35 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/columns.ts +32 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/index.ts +7 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/modules/examples/examples.ts +22 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/relations.ts +5 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/tables.ts +5 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/types.ts +7 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/schema/vo.ts +1 -0
- package/templates/hono-fullstack/libs/db/src/dialects/sqlite/utils.ts +8 -0
- package/templates/hono-fullstack/libs/db/src/shared/types.ts +26 -0
- package/templates/hono-fullstack/libs/db/tsconfig.json +10 -0
- package/templates/hono-fullstack/libs/db/vite.config.ts +21 -0
- package/templates/hono-fullstack/libs/definition/package.json +30 -0
- package/templates/hono-fullstack/libs/definition/src/backend.ts +2 -0
- package/templates/hono-fullstack/libs/definition/src/configs/defs/log.ts +53 -0
- package/templates/hono-fullstack/libs/definition/src/configs/defs/middleware.ts +16 -0
- package/templates/hono-fullstack/libs/definition/src/configs/defs/runtime.ts +11 -0
- package/templates/hono-fullstack/libs/definition/src/configs/defs/sqlite.ts +13 -0
- package/templates/hono-fullstack/libs/definition/src/configs/defs/worker.ts +29 -0
- package/templates/hono-fullstack/libs/definition/src/configs/index.ts +5 -0
- package/templates/hono-fullstack/libs/definition/src/configs/proto.ts +81 -0
- package/templates/hono-fullstack/libs/definition/src/configs/schema.ts +23 -0
- package/templates/hono-fullstack/libs/definition/src/frontend.ts +1 -0
- package/templates/hono-fullstack/libs/definition/src/modules/example/index.ts +16 -0
- package/templates/hono-fullstack/libs/definition/src/modules/index.ts +1 -0
- package/templates/hono-fullstack/libs/definition/tsconfig.json +5 -0
- package/templates/hono-fullstack/libs/definition/vite.config.ts +13 -0
- package/templates/hono-fullstack/oyo.config.json +12 -0
- package/templates/hono-fullstack/package.json +29 -0
- package/templates/hono-fullstack/pnpm-workspace.yaml +82 -0
- package/templates/hono-fullstack/tsconfig.json +4 -0
- package/templates/hono-fullstack/turbo.json +13 -0
- package/templates/hono-fullstack/vite.config.ts +13 -0
- package/templates/hono-server/.agents/skills/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/templates/hono-server/.agents/skills/ask-matt/SKILL.md +90 -0
- package/templates/hono-server/.agents/skills/ask-matt/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/code-review/SKILL.md +87 -0
- package/templates/hono-server/.agents/skills/code-review/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/codebase-design/DEEPENING.md +37 -0
- package/templates/hono-server/.agents/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/templates/hono-server/.agents/skills/codebase-design/SKILL.md +114 -0
- package/templates/hono-server/.agents/skills/codebase-design/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/confirm-requirements/SKILL.md +25 -0
- package/templates/hono-server/.agents/skills/diagnosing-bugs/SKILL.md +140 -0
- package/templates/hono-server/.agents/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/templates/hono-server/.agents/skills/domain-modeling/ADR-FORMAT.md +47 -0
- package/templates/hono-server/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/templates/hono-server/.agents/skills/domain-modeling/SKILL.md +74 -0
- package/templates/hono-server/.agents/skills/domain-modeling/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/grill-me/SKILL.md +7 -0
- package/templates/hono-server/.agents/skills/grill-me/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/grilling/SKILL.md +22 -0
- package/templates/hono-server/.agents/skills/grilling/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/implement/SKILL.md +15 -0
- package/templates/hono-server/.agents/skills/implement/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/templates/hono-server/.agents/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/templates/hono-server/.agents/skills/improve-codebase-architecture/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/prototype/LOGIC.md +67 -0
- package/templates/hono-server/.agents/skills/prototype/SKILL.md +26 -0
- package/templates/hono-server/.agents/skills/prototype/UI.md +112 -0
- package/templates/hono-server/.agents/skills/prototype/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/research/SKILL.md +12 -0
- package/templates/hono-server/.agents/skills/research/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/resolving-merge-conflicts/SKILL.md +14 -0
- package/templates/hono-server/.agents/skills/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/scoped-code-review/SKILL.md +113 -0
- package/templates/hono-server/.agents/skills/setup-matt-pocock-skills/SKILL.md +116 -0
- package/templates/hono-server/.agents/skills/setup-matt-pocock-skills/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/setup-matt-pocock-skills/domain.md +51 -0
- package/templates/hono-server/.agents/skills/setup-matt-pocock-skills/issue-tracker-github.md +45 -0
- package/templates/hono-server/.agents/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +46 -0
- package/templates/hono-server/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +30 -0
- package/templates/hono-server/.agents/skills/setup-matt-pocock-skills/triage-labels.md +15 -0
- package/templates/hono-server/.agents/skills/tdd/SKILL.md +38 -0
- package/templates/hono-server/.agents/skills/tdd/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/tdd/mocking.md +59 -0
- package/templates/hono-server/.agents/skills/tdd/tests.md +77 -0
- package/templates/hono-server/.agents/skills/teach/GLOSSARY-FORMAT.md +35 -0
- package/templates/hono-server/.agents/skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/templates/hono-server/.agents/skills/teach/MISSION-FORMAT.md +31 -0
- package/templates/hono-server/.agents/skills/teach/RESOURCES-FORMAT.md +32 -0
- package/templates/hono-server/.agents/skills/teach/SKILL.md +140 -0
- package/templates/hono-server/.agents/skills/teach/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/to-questionnaire/SKILL.md +53 -0
- package/templates/hono-server/.agents/skills/to-questionnaire/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/triage/AGENT-BRIEF.md +207 -0
- package/templates/hono-server/.agents/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/templates/hono-server/.agents/skills/triage/SKILL.md +112 -0
- package/templates/hono-server/.agents/skills/triage/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/wait-what/SKILL.md +7 -0
- package/templates/hono-server/.agents/skills/wait-what/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/wayfinder/SKILL.md +128 -0
- package/templates/hono-server/.agents/skills/wayfinder/agents/openai.yaml +5 -0
- package/templates/hono-server/.agents/skills/wizard/SKILL.md +44 -0
- package/templates/hono-server/.agents/skills/wizard/agents/openai.yaml +3 -0
- package/templates/hono-server/.agents/skills/wizard/template.sh +204 -0
- package/templates/hono-server/.agents/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/templates/hono-server/.agents/skills/writing-for-agents/SKILL.md +81 -0
- package/templates/hono-server/.agents/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/templates/hono-server/.changeset/README.md +8 -0
- package/templates/hono-server/.changeset/config.json +11 -0
- package/templates/hono-server/.editorconfig +31 -0
- package/templates/hono-server/.gitattributes +39 -0
- package/templates/hono-server/.vscode/extensions.json +5 -0
- package/templates/hono-server/.vscode/launch.json +25 -0
- package/templates/hono-server/.vscode/settings.json +24 -0
- package/templates/hono-server/AGENTS.md +43 -0
- package/templates/hono-server/README.md +31 -0
- package/templates/hono-server/_gitignore +30 -0
- package/templates/hono-server/apps/server/package.json +55 -0
- package/templates/hono-server/apps/server/src/adapters/nodejs/bootstrap.ts +61 -0
- package/templates/hono-server/apps/server/src/adapters/nodejs/config.ts +53 -0
- package/templates/hono-server/apps/server/src/adapters/nodejs/entry.ts +40 -0
- package/templates/hono-server/apps/server/src/app.test.ts +52 -0
- package/templates/hono-server/apps/server/src/app.ts +45 -0
- package/templates/hono-server/apps/server/src/infra/logger/getter.ts +74 -0
- package/templates/hono-server/apps/server/src/infra/logger/pino.ts +122 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/constants.ts +23 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/errors.ts +40 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/example/runner.test.ts +56 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/example/runner.ts +185 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/example/worker-entry.ts +34 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/types.ts +113 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/utils/business-error.ts +19 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/utils/message.ts +46 -0
- package/templates/hono-server/apps/server/src/infra/work-runner/utils/transfer.ts +144 -0
- package/templates/hono-server/apps/server/src/middlewares/cors.ts +26 -0
- package/templates/hono-server/apps/server/src/modules/example/contracts.ts +5 -0
- package/templates/hono-server/apps/server/src/modules/example/routes.ts +46 -0
- package/templates/hono-server/apps/server/src/modules/example/work-task/tasks.test.ts +37 -0
- package/templates/hono-server/apps/server/src/modules/example/work-task/tasks.ts +35 -0
- package/templates/hono-server/apps/server/src/shared/app/types.ts +40 -0
- package/templates/hono-server/apps/server/src/shared/errors/constants.ts +152 -0
- package/templates/hono-server/apps/server/src/shared/errors/defs/index.ts +15 -0
- package/templates/hono-server/apps/server/src/shared/errors/handler.ts +16 -0
- package/templates/hono-server/apps/server/src/shared/errors/log.ts +67 -0
- package/templates/hono-server/apps/server/src/shared/errors/proto.ts +42 -0
- package/templates/hono-server/apps/server/src/shared/errors/types.ts +29 -0
- package/templates/hono-server/apps/server/src/shared/network/fetcher.ts +35 -0
- package/templates/hono-server/apps/server/src/shared/network/openapi/index.ts +50 -0
- package/templates/hono-server/apps/server/src/shared/network/openapi/res-defs.ts +39 -0
- package/templates/hono-server/apps/server/src/shared/network/types.ts +3 -0
- package/templates/hono-server/apps/server/src/shared/network/utils.ts +36 -0
- package/templates/hono-server/apps/server/src/shared/network/validator.ts +21 -0
- package/templates/hono-server/apps/server/src/shared/utils/index.ts +30 -0
- package/templates/hono-server/apps/server/src/virtual-modules.d.ts +5 -0
- package/templates/hono-server/apps/server/tsconfig.json +16 -0
- package/templates/hono-server/apps/server/vite.config.ts +79 -0
- package/templates/hono-server/docker/.env.example +4 -0
- package/templates/hono-server/docker/README.md +5 -0
- package/templates/hono-server/docker/local/compose.local.app.yml +21 -0
- package/templates/hono-server/docker/local/local-app-deploy.sh +8 -0
- package/templates/hono-server/docker/local/local-app-down.sh +8 -0
- package/templates/hono-server/docker/prod/.env.example.server +11 -0
- package/templates/hono-server/docker/prod/compose.prod.stack.yml +21 -0
- package/templates/hono-server/docker/prod/update.sh +7 -0
- package/templates/hono-server/docker/server.Dockerfile +40 -0
- package/templates/hono-server/docker/server.Dockerfile.dockerignore +23 -0
- package/templates/hono-server/docs/agents/domain.md +41 -0
- package/templates/hono-server/docs/agents/issue-tracker.md +30 -0
- package/templates/hono-server/docs/agents/triage-labels.md +15 -0
- package/templates/hono-server/envs/.env.example.server +12 -0
- package/templates/hono-server/libs/definition/package.json +29 -0
- package/templates/hono-server/libs/definition/src/backend.ts +2 -0
- package/templates/hono-server/libs/definition/src/configs/defs/log.ts +53 -0
- package/templates/hono-server/libs/definition/src/configs/defs/middleware.ts +16 -0
- package/templates/hono-server/libs/definition/src/configs/defs/runtime.ts +11 -0
- package/templates/hono-server/libs/definition/src/configs/defs/worker.ts +29 -0
- package/templates/hono-server/libs/definition/src/configs/index.ts +4 -0
- package/templates/hono-server/libs/definition/src/configs/proto.ts +81 -0
- package/templates/hono-server/libs/definition/src/configs/schema.ts +23 -0
- package/templates/hono-server/libs/definition/src/modules/example/index.ts +16 -0
- package/templates/hono-server/libs/definition/src/modules/index.ts +1 -0
- package/templates/hono-server/libs/definition/tsconfig.json +5 -0
- package/templates/hono-server/libs/definition/vite.config.ts +12 -0
- package/templates/hono-server/oyo.config.json +11 -0
- package/templates/hono-server/package.json +26 -0
- package/templates/hono-server/pnpm-workspace.yaml +57 -0
- package/templates/hono-server/scripts/.env.example.docker +4 -0
- package/templates/hono-server/scripts/build-docker-server.sh +13 -0
- package/templates/hono-server/scripts/common.sh +20 -0
- package/templates/hono-server/tsconfig.json +4 -0
- package/templates/hono-server/turbo.json +13 -0
- package/templates/hono-server/vite.config.ts +13 -0
- package/templates/monorepo/.agents/skills/ask-matt/PHASE-BOUNDARIES.md +55 -0
- package/templates/monorepo/.agents/skills/ask-matt/SKILL.md +90 -0
- package/templates/monorepo/.agents/skills/ask-matt/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/code-review/SKILL.md +87 -0
- package/templates/monorepo/.agents/skills/code-review/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/codebase-design/DEEPENING.md +37 -0
- package/templates/monorepo/.agents/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/templates/monorepo/.agents/skills/codebase-design/SKILL.md +114 -0
- package/templates/monorepo/.agents/skills/codebase-design/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/confirm-requirements/SKILL.md +25 -0
- package/templates/monorepo/.agents/skills/diagnosing-bugs/SKILL.md +140 -0
- package/templates/monorepo/.agents/skills/diagnosing-bugs/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/diagnosing-bugs/scripts/hitl-loop.template.sh +44 -0
- package/templates/monorepo/.agents/skills/domain-modeling/ADR-FORMAT.md +47 -0
- package/templates/monorepo/.agents/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/templates/monorepo/.agents/skills/domain-modeling/SKILL.md +74 -0
- package/templates/monorepo/.agents/skills/domain-modeling/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/grill-me/SKILL.md +7 -0
- package/templates/monorepo/.agents/skills/grill-me/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/grilling/SKILL.md +22 -0
- package/templates/monorepo/.agents/skills/grilling/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/implement/SKILL.md +15 -0
- package/templates/monorepo/.agents/skills/implement/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
- package/templates/monorepo/.agents/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/templates/monorepo/.agents/skills/improve-codebase-architecture/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/prototype/LOGIC.md +67 -0
- package/templates/monorepo/.agents/skills/prototype/SKILL.md +26 -0
- package/templates/monorepo/.agents/skills/prototype/UI.md +112 -0
- package/templates/monorepo/.agents/skills/prototype/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/research/SKILL.md +12 -0
- package/templates/monorepo/.agents/skills/research/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/resolving-merge-conflicts/SKILL.md +14 -0
- package/templates/monorepo/.agents/skills/resolving-merge-conflicts/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/scoped-code-review/SKILL.md +113 -0
- package/templates/monorepo/.agents/skills/setup-matt-pocock-skills/SKILL.md +116 -0
- package/templates/monorepo/.agents/skills/setup-matt-pocock-skills/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/setup-matt-pocock-skills/domain.md +51 -0
- package/templates/monorepo/.agents/skills/setup-matt-pocock-skills/issue-tracker-github.md +45 -0
- package/templates/monorepo/.agents/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +46 -0
- package/templates/monorepo/.agents/skills/setup-matt-pocock-skills/issue-tracker-local.md +30 -0
- package/templates/monorepo/.agents/skills/setup-matt-pocock-skills/triage-labels.md +15 -0
- package/templates/monorepo/.agents/skills/tdd/SKILL.md +38 -0
- package/templates/monorepo/.agents/skills/tdd/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/tdd/mocking.md +59 -0
- package/templates/monorepo/.agents/skills/tdd/tests.md +77 -0
- package/templates/monorepo/.agents/skills/teach/GLOSSARY-FORMAT.md +35 -0
- package/templates/monorepo/.agents/skills/teach/LEARNING-RECORD-FORMAT.md +46 -0
- package/templates/monorepo/.agents/skills/teach/MISSION-FORMAT.md +31 -0
- package/templates/monorepo/.agents/skills/teach/RESOURCES-FORMAT.md +32 -0
- package/templates/monorepo/.agents/skills/teach/SKILL.md +140 -0
- package/templates/monorepo/.agents/skills/teach/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/to-questionnaire/SKILL.md +53 -0
- package/templates/monorepo/.agents/skills/to-questionnaire/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/triage/AGENT-BRIEF.md +207 -0
- package/templates/monorepo/.agents/skills/triage/OUT-OF-SCOPE.md +105 -0
- package/templates/monorepo/.agents/skills/triage/SKILL.md +112 -0
- package/templates/monorepo/.agents/skills/triage/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/wait-what/SKILL.md +7 -0
- package/templates/monorepo/.agents/skills/wait-what/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/wayfinder/SKILL.md +128 -0
- package/templates/monorepo/.agents/skills/wayfinder/agents/openai.yaml +5 -0
- package/templates/monorepo/.agents/skills/wizard/SKILL.md +44 -0
- package/templates/monorepo/.agents/skills/wizard/agents/openai.yaml +3 -0
- package/templates/monorepo/.agents/skills/wizard/template.sh +204 -0
- package/templates/monorepo/.agents/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
- package/templates/monorepo/.agents/skills/writing-for-agents/SKILL.md +81 -0
- package/templates/monorepo/.agents/skills/writing-for-agents/agents/openai.yaml +3 -0
- package/templates/monorepo/.editorconfig +31 -0
- package/templates/monorepo/.gitattributes +39 -0
- package/templates/monorepo/.vscode/extensions.json +5 -0
- package/templates/monorepo/.vscode/launch.json +25 -0
- package/templates/monorepo/.vscode/settings.json +3 -1
- package/templates/monorepo/AGENTS.md +43 -0
- package/templates/monorepo/README.md +27 -0
- package/templates/monorepo/_gitignore +8 -0
- package/templates/monorepo/docs/agents/domain.md +41 -0
- package/templates/monorepo/docs/agents/issue-tracker.md +30 -0
- package/templates/monorepo/docs/agents/triage-labels.md +15 -0
- package/templates/monorepo/envs/.env.example +1 -0
- package/templates/monorepo/oyo.config.json +11 -0
- package/templates/monorepo/package.json +24 -0
- package/templates/monorepo/pnpm-workspace.yaml +32 -0
- package/templates/monorepo/tsconfig.json +4 -0
- package/templates/monorepo/turbo.json +13 -0
- package/templates/monorepo/vite.config.ts +13 -0
- package/templates/monorepo/.vite-hooks/pre-commit +0 -1
- /package/templates/{monorepo/_editorconfig → hono-fullstack/.editorconfig} +0 -0
- /package/templates/{monorepo/_gitattributes → hono-fullstack/.gitattributes} +0 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { type WorkerConfig } from '@repo/definition/backend'
|
|
2
|
+
import type {
|
|
3
|
+
AppWorkRunner,
|
|
4
|
+
RunnerTaskInvocation,
|
|
5
|
+
RunnerTaskParams,
|
|
6
|
+
RunnerTaskRunOptions,
|
|
7
|
+
RunnerTaskRunResult,
|
|
8
|
+
WorkerMessage,
|
|
9
|
+
} from '../types'
|
|
10
|
+
import { WorkerErrorCode, WorkSubject } from '../constants'
|
|
11
|
+
import { isWorkerTaskError, WorkerTaskError } from '../errors'
|
|
12
|
+
import Piscina from 'piscina'
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
import { MessageChannel, type MessagePort } from 'node:worker_threads'
|
|
15
|
+
import workersDir from 'virtual:workers'
|
|
16
|
+
import { getLogger } from '@/infra/logger/getter'
|
|
17
|
+
import type * as ExampleWorker from './worker-entry'
|
|
18
|
+
import { handleWorkerMessage } from '../utils/message'
|
|
19
|
+
|
|
20
|
+
const taskNameMap = {
|
|
21
|
+
[WorkSubject.PROCESS_EXAMPLE]: 'executeExampleTask',
|
|
22
|
+
} satisfies Partial<Record<WorkSubject, keyof typeof ExampleWorker>>
|
|
23
|
+
|
|
24
|
+
export class ExampleWorkRunner implements AppWorkRunner {
|
|
25
|
+
static #instance: ExampleWorkRunner | null = null
|
|
26
|
+
readonly #pool: Piscina
|
|
27
|
+
readonly #config: WorkerConfig
|
|
28
|
+
|
|
29
|
+
private constructor(config: WorkerConfig) {
|
|
30
|
+
if (!config) {
|
|
31
|
+
throw new Error('[ExampleWorkRunner] config is required')
|
|
32
|
+
}
|
|
33
|
+
this.#pool = new Piscina({
|
|
34
|
+
filename: path.join(workersDir, 'example-worker.mjs'),
|
|
35
|
+
minThreads: config.minThreads,
|
|
36
|
+
maxThreads: config.maxThreads,
|
|
37
|
+
concurrentTasksPerWorker: config.concurrentTasksPerWorker,
|
|
38
|
+
maxQueue: config.maxQueue,
|
|
39
|
+
})
|
|
40
|
+
this.#config = config
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static getInstance(config: WorkerConfig): ExampleWorkRunner {
|
|
44
|
+
if (!this.#instance) {
|
|
45
|
+
getLogger().info({ worker: config }, 'ExampleWorkRunner initialized')
|
|
46
|
+
this.#instance = new ExampleWorkRunner(config)
|
|
47
|
+
}
|
|
48
|
+
return this.#instance
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async run<TSubject extends WorkSubject>(
|
|
52
|
+
taskParams: RunnerTaskParams<TSubject>,
|
|
53
|
+
options?: RunnerTaskRunOptions,
|
|
54
|
+
): Promise<RunnerTaskRunResult<TSubject>> {
|
|
55
|
+
const targetName = taskNameMap[taskParams.subject]
|
|
56
|
+
if (!targetName) {
|
|
57
|
+
throw new Error(`[ExampleWorkRunner] task name not found: ${taskParams.subject}`)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let timeoutTimer: ReturnType<typeof setTimeout> | undefined
|
|
61
|
+
let result: RunnerTaskRunResult<TSubject>
|
|
62
|
+
let workerMessagePort: MessagePort | undefined
|
|
63
|
+
|
|
64
|
+
const { signal: optSignal, transferList } = options || {}
|
|
65
|
+
const logger = getLogger()
|
|
66
|
+
// pool当前状态
|
|
67
|
+
const poolStats = {
|
|
68
|
+
idleThreads: this.#pool.idleThreads,
|
|
69
|
+
threads: this.#pool.threads.length,
|
|
70
|
+
queueSize: this.#pool.queueSize,
|
|
71
|
+
}
|
|
72
|
+
logger.assign({
|
|
73
|
+
pool: poolStats,
|
|
74
|
+
subject: taskParams.subject,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
optSignal?.throwIfAborted()
|
|
79
|
+
|
|
80
|
+
// 判断队列已满
|
|
81
|
+
const enableQueue = this.#pool.options.maxQueue > 0
|
|
82
|
+
if (
|
|
83
|
+
(enableQueue && poolStats.queueSize >= this.#pool.options.maxQueue) ||
|
|
84
|
+
(!enableQueue && poolStats.idleThreads <= 0)
|
|
85
|
+
) {
|
|
86
|
+
throw new WorkerTaskError(WorkerErrorCode.QUEUE_FULL, 'Worker queue is full', {
|
|
87
|
+
enableQueue,
|
|
88
|
+
queueSize: poolStats.queueSize,
|
|
89
|
+
maxQueue: this.#config.maxQueue,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
logger.debug('Worker task dispatching')
|
|
94
|
+
|
|
95
|
+
// 建立收发端
|
|
96
|
+
const { port1: messagePort, port2 } = new MessageChannel()
|
|
97
|
+
workerMessagePort = port2
|
|
98
|
+
messagePort.on('message', (message: WorkerMessage) =>
|
|
99
|
+
handleWorkerMessage(message, logger, { subject: taskParams.subject }),
|
|
100
|
+
)
|
|
101
|
+
messagePort.unref()
|
|
102
|
+
|
|
103
|
+
// 设置任务信号和超时
|
|
104
|
+
let taskSignal = optSignal
|
|
105
|
+
let timeoutPromise: Promise<never> | undefined
|
|
106
|
+
if (this.#config.workerTaskTimeoutMs > 0) {
|
|
107
|
+
const timeoutController = new AbortController()
|
|
108
|
+
taskSignal = optSignal
|
|
109
|
+
? AbortSignal.any([optSignal, timeoutController.signal])
|
|
110
|
+
: timeoutController.signal
|
|
111
|
+
timeoutPromise = new Promise<never>((_, reject) => {
|
|
112
|
+
timeoutTimer = setTimeout(() => {
|
|
113
|
+
const error = new WorkerTaskError(
|
|
114
|
+
WorkerErrorCode.TASK_TIMEOUT,
|
|
115
|
+
`Worker task timed out after ${this.#config.workerTaskTimeoutMs}ms`,
|
|
116
|
+
{ timeoutMs: this.#config.workerTaskTimeoutMs },
|
|
117
|
+
)
|
|
118
|
+
reject(error)
|
|
119
|
+
timeoutController.abort(error)
|
|
120
|
+
}, this.#config.workerTaskTimeoutMs)
|
|
121
|
+
timeoutTimer.unref()
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 开始调用
|
|
126
|
+
const invocation: RunnerTaskInvocation<(typeof taskParams)['input']> = {
|
|
127
|
+
input: taskParams.input,
|
|
128
|
+
messagePort: workerMessagePort,
|
|
129
|
+
}
|
|
130
|
+
const taskPromise = this.#pool.run(invocation, {
|
|
131
|
+
name: targetName,
|
|
132
|
+
signal: taskSignal,
|
|
133
|
+
transferList: [...(transferList || []), workerMessagePort],
|
|
134
|
+
}) as Promise<RunnerTaskRunResult<TSubject>>
|
|
135
|
+
|
|
136
|
+
result = timeoutPromise
|
|
137
|
+
? await Promise.race([taskPromise, timeoutPromise])
|
|
138
|
+
: await taskPromise
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (isWorkerTaskError(error)) {
|
|
141
|
+
result = {
|
|
142
|
+
ok: false,
|
|
143
|
+
error: error.serialize(),
|
|
144
|
+
}
|
|
145
|
+
} else if (error instanceof Error && error.name === 'AbortError') {
|
|
146
|
+
result = {
|
|
147
|
+
ok: false,
|
|
148
|
+
error: new WorkerTaskError(
|
|
149
|
+
WorkerErrorCode.TASK_ABORTED,
|
|
150
|
+
error.message,
|
|
151
|
+
undefined,
|
|
152
|
+
).serialize(),
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
result = {
|
|
156
|
+
ok: false,
|
|
157
|
+
error: new WorkerTaskError(
|
|
158
|
+
WorkerErrorCode.UNKNOWN,
|
|
159
|
+
'Worker task failed unexpectedly',
|
|
160
|
+
undefined,
|
|
161
|
+
error,
|
|
162
|
+
).serialize(),
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
} finally {
|
|
166
|
+
if (timeoutTimer) clearTimeout(timeoutTimer)
|
|
167
|
+
try {
|
|
168
|
+
workerMessagePort?.close()
|
|
169
|
+
} catch {
|
|
170
|
+
// 忽略已 transfer/detached 端口的 close 异常
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
logger[result.ok ? 'info' : 'error'](
|
|
175
|
+
{ subject: taskParams.subject, ok: result.ok },
|
|
176
|
+
`Worker task ${result.ok ? 'completed' : 'failed'}`,
|
|
177
|
+
)
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async shutdown(): Promise<void> {
|
|
182
|
+
await this.#pool.close()
|
|
183
|
+
if (ExampleWorkRunner.#instance === this) ExampleWorkRunner.#instance = null
|
|
184
|
+
}
|
|
185
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** WORKER 打包入口 */
|
|
2
|
+
import type { RunnerTaskInvocation, RunnerTaskResult } from '../types'
|
|
3
|
+
import {
|
|
4
|
+
executeExampleTask as runExampleTask,
|
|
5
|
+
type ExampleRunnerTaskModel,
|
|
6
|
+
} from '@/modules/example/work-task/tasks'
|
|
7
|
+
import { isWorkerTaskBusinessError } from '../utils/business-error'
|
|
8
|
+
|
|
9
|
+
export async function executeExampleTask({
|
|
10
|
+
input,
|
|
11
|
+
messagePort,
|
|
12
|
+
}: RunnerTaskInvocation<ExampleRunnerTaskModel['input']>): Promise<
|
|
13
|
+
RunnerTaskResult<ExampleRunnerTaskModel['output'], ExampleRunnerTaskModel['errorCode']>
|
|
14
|
+
> {
|
|
15
|
+
try {
|
|
16
|
+
const result = await runExampleTask(input, messagePort)
|
|
17
|
+
messagePort.postMessage({ type: 'result', data: result.data })
|
|
18
|
+
return result
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (!isWorkerTaskBusinessError(error)) throw error
|
|
21
|
+
const result = {
|
|
22
|
+
ok: false,
|
|
23
|
+
error: {
|
|
24
|
+
code: 'EXAMPLE_PROCESS_FAILED' as const,
|
|
25
|
+
message: error.message,
|
|
26
|
+
...(error.details ? { details: error.details } : {}),
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
messagePort.postMessage({ type: 'error', error: result.error })
|
|
30
|
+
return result
|
|
31
|
+
} finally {
|
|
32
|
+
messagePort.close()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { ExampleRunnerTaskModel } from '@/modules/example/work-task/tasks'
|
|
2
|
+
import type { MessagePort, Transferable } from 'node:worker_threads'
|
|
3
|
+
import type { WorkerErrorCode, WorkSubject } from './constants'
|
|
4
|
+
|
|
5
|
+
// export const WorkerInvocationType = Object.freeze({
|
|
6
|
+
// READY: 'READY',
|
|
7
|
+
// TASK: 'TASK',
|
|
8
|
+
// } as const)
|
|
9
|
+
// export type WorkerInvocationType = (typeof WorkerInvocationType)[keyof typeof WorkerInvocationType]
|
|
10
|
+
|
|
11
|
+
// export const WorkerSignalType = Object.freeze({
|
|
12
|
+
// STARTED: 'started',
|
|
13
|
+
// } as const)
|
|
14
|
+
// export type WorkerSignalType = (typeof WorkerSignalType)[keyof typeof WorkerSignalType]
|
|
15
|
+
|
|
16
|
+
export type DefineRunnerTaskModel<
|
|
17
|
+
TSubject extends WorkSubject,
|
|
18
|
+
TInput,
|
|
19
|
+
TOutput,
|
|
20
|
+
TTaskErrorCode extends string = string,
|
|
21
|
+
> = {
|
|
22
|
+
subject: TSubject
|
|
23
|
+
input: TInput
|
|
24
|
+
output: TOutput
|
|
25
|
+
errorCode: TTaskErrorCode | WorkerErrorCode
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type AnyRunnerTaskModel = ExampleRunnerTaskModel
|
|
29
|
+
|
|
30
|
+
export type GetRunnerTaskModel<TSubject extends WorkSubject> = Extract<
|
|
31
|
+
AnyRunnerTaskModel,
|
|
32
|
+
{ subject: TSubject }
|
|
33
|
+
>
|
|
34
|
+
|
|
35
|
+
export type RunnerTaskParams<TSubject extends WorkSubject> = Pick<
|
|
36
|
+
GetRunnerTaskModel<TSubject>,
|
|
37
|
+
'input' | 'subject'
|
|
38
|
+
>
|
|
39
|
+
export type RunnerTaskInput<TSubject extends WorkSubject> = GetRunnerTaskModel<TSubject>['input']
|
|
40
|
+
export type RunnerTaskOutput<TSubject extends WorkSubject> = GetRunnerTaskModel<TSubject>['output']
|
|
41
|
+
export type RunnerTaskErrorCode<TSubject extends WorkSubject> =
|
|
42
|
+
GetRunnerTaskModel<TSubject>['errorCode']
|
|
43
|
+
|
|
44
|
+
export type RunnerTaskInvocation<TInput> = {
|
|
45
|
+
input: TInput
|
|
46
|
+
messagePort: MessagePort
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type RunnerTaskJsonValue =
|
|
50
|
+
| string
|
|
51
|
+
| number
|
|
52
|
+
| boolean
|
|
53
|
+
| null
|
|
54
|
+
| readonly RunnerTaskJsonValue[]
|
|
55
|
+
| { readonly [key: string]: RunnerTaskJsonValue }
|
|
56
|
+
|
|
57
|
+
/** Worker 通信中的错误详情必须保持为可结构化克隆、可 JSON 序列化的普通数据。 */
|
|
58
|
+
export type RunnerTaskErrorDetails = Readonly<Record<string, RunnerTaskJsonValue>>
|
|
59
|
+
|
|
60
|
+
export type WorkerMessage =
|
|
61
|
+
| {
|
|
62
|
+
type: 'log'
|
|
63
|
+
level: 'debug' | 'info' | 'warn' | 'error'
|
|
64
|
+
message: string
|
|
65
|
+
data?: RunnerTaskErrorDetails
|
|
66
|
+
}
|
|
67
|
+
| {
|
|
68
|
+
type: 'progress'
|
|
69
|
+
value: number
|
|
70
|
+
message?: string
|
|
71
|
+
}
|
|
72
|
+
| {
|
|
73
|
+
type: 'result'
|
|
74
|
+
data: RunnerTaskJsonValue
|
|
75
|
+
}
|
|
76
|
+
| {
|
|
77
|
+
type: 'error'
|
|
78
|
+
error: RunnerTaskError
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type RunnerTaskError<TCode extends string = string> = {
|
|
82
|
+
code: TCode
|
|
83
|
+
/** 仅供服务端诊断,不应直接作为 HTTP 响应文案。 */
|
|
84
|
+
message: string
|
|
85
|
+
details?: RunnerTaskErrorDetails
|
|
86
|
+
cause?: unknown
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type RunnerTaskResult<TData, TCode extends string = string> =
|
|
90
|
+
| { ok: true; data: TData }
|
|
91
|
+
| { ok: false; error: RunnerTaskError<TCode> }
|
|
92
|
+
|
|
93
|
+
export type RunnerTaskRunResult<TSubject extends WorkSubject> = RunnerTaskResult<
|
|
94
|
+
RunnerTaskOutput<TSubject>,
|
|
95
|
+
RunnerTaskErrorCode<TSubject>
|
|
96
|
+
>
|
|
97
|
+
|
|
98
|
+
export type RunnerTaskRunOptions = {
|
|
99
|
+
signal?: AbortSignal
|
|
100
|
+
transferList?: Transferable[]
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface AppWorkRunner {
|
|
104
|
+
run<TSubject extends WorkSubject>(
|
|
105
|
+
task: RunnerTaskParams<TSubject>,
|
|
106
|
+
options?: RunnerTaskRunOptions,
|
|
107
|
+
): Promise<RunnerTaskRunResult<TSubject>>
|
|
108
|
+
shutdown(): Promise<void>
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 已识别的公共 Worker 错误 Code 由具体任务与业务错误 Code 组合:
|
|
112
|
+
// 队列满、任务超时、Worker 暂不可用等返回失败 DTO;
|
|
113
|
+
// 未识别的 Piscina 异常和程序缺陷仍保持 Promise rejection,不能在此兜底吞掉。
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { hasProp } from '@/shared/utils'
|
|
2
|
+
import type { RunnerTaskErrorDetails } from '../types'
|
|
3
|
+
|
|
4
|
+
export class WorkerTaskBusinessError extends Error {
|
|
5
|
+
readonly details?: RunnerTaskErrorDetails
|
|
6
|
+
|
|
7
|
+
constructor(message: string, details?: RunnerTaskErrorDetails, cause?: unknown) {
|
|
8
|
+
super(message, { cause })
|
|
9
|
+
this.name = 'WorkerTaskBusinessError'
|
|
10
|
+
this.details = details
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function isWorkerTaskBusinessError(error: unknown): error is WorkerTaskBusinessError {
|
|
15
|
+
return (
|
|
16
|
+
error instanceof WorkerTaskBusinessError ||
|
|
17
|
+
(error instanceof Error && hasProp(error, 'name') && error.name === 'WorkerTaskBusinessError')
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AppLogger } from '@/shared/app/types'
|
|
2
|
+
import type { RunnerTaskErrorDetails, WorkerMessage } from '../types'
|
|
3
|
+
import type { MessagePort } from 'node:worker_threads'
|
|
4
|
+
import type { WorkSubject } from '../constants'
|
|
5
|
+
|
|
6
|
+
export function workerLog(
|
|
7
|
+
messagePort: MessagePort,
|
|
8
|
+
level: Extract<WorkerMessage, { type: 'log' }>['level'],
|
|
9
|
+
message: string,
|
|
10
|
+
data?: RunnerTaskErrorDetails,
|
|
11
|
+
): void {
|
|
12
|
+
messagePort.postMessage({ type: 'log', level, message, data } satisfies WorkerMessage)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function workerProgress(messagePort: MessagePort, value: number, message?: string): void {
|
|
16
|
+
messagePort.postMessage({ type: 'progress', value, message } satisfies WorkerMessage)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function handleWorkerMessage(
|
|
20
|
+
message: WorkerMessage,
|
|
21
|
+
logger: AppLogger,
|
|
22
|
+
context: {
|
|
23
|
+
subject: WorkSubject
|
|
24
|
+
},
|
|
25
|
+
): void {
|
|
26
|
+
switch (message.type) {
|
|
27
|
+
case 'log':
|
|
28
|
+
logger[message.level]({ subject: context.subject, worker: message.data }, message.message)
|
|
29
|
+
break
|
|
30
|
+
case 'progress':
|
|
31
|
+
logger.debug(
|
|
32
|
+
{ subject: context.subject, progress: message.value },
|
|
33
|
+
message.message || 'Worker progress',
|
|
34
|
+
)
|
|
35
|
+
break
|
|
36
|
+
case 'result':
|
|
37
|
+
logger.debug({ subject: context.subject }, 'Worker result delivered')
|
|
38
|
+
break
|
|
39
|
+
case 'error':
|
|
40
|
+
logger.warn(
|
|
41
|
+
{ subject: context.subject, workerError: message.error },
|
|
42
|
+
'Worker error delivered',
|
|
43
|
+
)
|
|
44
|
+
break
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { isMarkedAsUntransferable } from 'node:worker_threads'
|
|
2
|
+
|
|
3
|
+
export interface TransferableResult<T> {
|
|
4
|
+
value: T
|
|
5
|
+
transferable?: ArrayBuffer
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 排除不需要遍历其属性的原生系统对象(交由 V8 结构化克隆内部处理)
|
|
10
|
+
*/
|
|
11
|
+
function isCloneableLeaf(value: unknown): boolean {
|
|
12
|
+
return (
|
|
13
|
+
value instanceof Date ||
|
|
14
|
+
value instanceof RegExp ||
|
|
15
|
+
value instanceof Error ||
|
|
16
|
+
(typeof URL !== 'undefined' && value instanceof URL)
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 转换 ArrayBufferView
|
|
22
|
+
*/
|
|
23
|
+
export function toTransferable<T extends ArrayBufferView>(view: T): TransferableResult<T> {
|
|
24
|
+
const buffer = view.buffer
|
|
25
|
+
|
|
26
|
+
// 1. SharedArrayBuffer 或已显式标记为不可转移的 Buffer,原样返回(由系统克隆)
|
|
27
|
+
if (!(buffer instanceof ArrayBuffer) || isMarkedAsUntransferable(buffer)) {
|
|
28
|
+
return { value: view }
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 2. 独占且干净的 Buffer:直接转移,零拷贝
|
|
32
|
+
if (view.byteOffset === 0 && view.byteLength === buffer.byteLength) {
|
|
33
|
+
return { value: view, transferable: buffer }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 3. 处于切片/池化状态:切片降级,提取独立 ArrayBuffer
|
|
37
|
+
const newBuffer = buffer.slice(view.byteOffset, view.byteOffset + view.byteLength)
|
|
38
|
+
|
|
39
|
+
// 4. 重新包装 View,修复 Node.js Buffer 的构造函数废弃警告
|
|
40
|
+
let newView: T
|
|
41
|
+
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(view)) {
|
|
42
|
+
newView = Buffer.from(newBuffer) as unknown as T
|
|
43
|
+
} else {
|
|
44
|
+
const Ctor = view.constructor as new (buf: ArrayBuffer) => T
|
|
45
|
+
newView = new Ctor(newBuffer)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
value: newView,
|
|
50
|
+
transferable: newBuffer,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 【主入口】:深层提取 Transferables
|
|
56
|
+
*/
|
|
57
|
+
export function extractTransferables(input: any): {
|
|
58
|
+
input: any
|
|
59
|
+
transferList: ArrayBuffer[]
|
|
60
|
+
} {
|
|
61
|
+
const transferables = new Set<ArrayBuffer>()
|
|
62
|
+
const extracted = new WeakMap<object, unknown>()
|
|
63
|
+
|
|
64
|
+
function extract(value: any): any {
|
|
65
|
+
if (!value || typeof value !== 'object') return value
|
|
66
|
+
|
|
67
|
+
// 命中缓存(环状引用 / 重复对象)
|
|
68
|
+
const cached = extracted.get(value)
|
|
69
|
+
if (cached !== undefined) return cached
|
|
70
|
+
|
|
71
|
+
// 裸 ArrayBuffer
|
|
72
|
+
if (value instanceof ArrayBuffer) {
|
|
73
|
+
if (!isMarkedAsUntransferable(value)) {
|
|
74
|
+
transferables.add(value)
|
|
75
|
+
}
|
|
76
|
+
extracted.set(value, value)
|
|
77
|
+
return value
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ArrayBufferView (Buffer, TypedArray, DataView)
|
|
81
|
+
if (ArrayBuffer.isView(value)) {
|
|
82
|
+
const { value: transferableView, transferable } = toTransferable(value)
|
|
83
|
+
if (transferable) {
|
|
84
|
+
transferables.add(transferable)
|
|
85
|
+
}
|
|
86
|
+
extracted.set(value, transferableView)
|
|
87
|
+
return transferableView
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 遇到原生特殊对象,直接跳过深入遍历
|
|
91
|
+
if (isCloneableLeaf(value)) {
|
|
92
|
+
extracted.set(value, value)
|
|
93
|
+
return value
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 场景 4:普通数组
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
const array: unknown[] = []
|
|
99
|
+
extracted.set(value, array)
|
|
100
|
+
for (let i = 0; i < value.length; i++) {
|
|
101
|
+
array.push(extract(value[i]))
|
|
102
|
+
}
|
|
103
|
+
return array
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 场景 5:Map 和 Set
|
|
107
|
+
if (value instanceof Map) {
|
|
108
|
+
const map = new Map()
|
|
109
|
+
extracted.set(value, map)
|
|
110
|
+
for (const [k, v] of value) {
|
|
111
|
+
map.set(extract(k), extract(v))
|
|
112
|
+
}
|
|
113
|
+
return map
|
|
114
|
+
}
|
|
115
|
+
if (value instanceof Set) {
|
|
116
|
+
const set = new Set()
|
|
117
|
+
extracted.set(value, set)
|
|
118
|
+
for (const v of value) {
|
|
119
|
+
set.add(extract(v))
|
|
120
|
+
}
|
|
121
|
+
return set
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// 场景 6:普通对象 & 自定义类实例(支持 Symbol 键)
|
|
125
|
+
const proto = Object.getPrototypeOf(value)
|
|
126
|
+
if (proto === null || proto === Object.prototype || (proto && proto.constructor !== Object)) {
|
|
127
|
+
const object: Record<string | symbol, unknown> = {}
|
|
128
|
+
extracted.set(value, object)
|
|
129
|
+
|
|
130
|
+
const keys = Reflect.ownKeys(value)
|
|
131
|
+
for (const key of keys) {
|
|
132
|
+
object[key] = extract(value[key as keyof typeof value])
|
|
133
|
+
}
|
|
134
|
+
return object
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return value
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
input: extract(input),
|
|
142
|
+
transferList: [...transferables],
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AppEnv } from '@/shared/app/types'
|
|
2
|
+
import { cors } from 'hono/cors'
|
|
3
|
+
import { createMiddleware } from 'hono/factory'
|
|
4
|
+
|
|
5
|
+
export const corsMiddleware = () => {
|
|
6
|
+
return createMiddleware<AppEnv>(async (c, next) => {
|
|
7
|
+
const corsConfig = c.get('appConfig')
|
|
8
|
+
const domains = corsConfig.middleware.cors.domains
|
|
9
|
+
const middleware = cors({
|
|
10
|
+
origin: (origin) => {
|
|
11
|
+
const isAllowedDomain = domains.some((domain) => {
|
|
12
|
+
try {
|
|
13
|
+
return domain === '*' || new URL(origin).host === domain
|
|
14
|
+
} catch {
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
return isAllowedDomain ? origin : null
|
|
19
|
+
},
|
|
20
|
+
allowHeaders: ['Content-Type'],
|
|
21
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
22
|
+
credentials: true,
|
|
23
|
+
})
|
|
24
|
+
return middleware(c, next)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Hono } from 'hono'
|
|
2
|
+
import { describeRoute } from 'hono-openapi'
|
|
3
|
+
import * as v from 'valibot'
|
|
4
|
+
import type { AppEnv } from '@/shared/app/types'
|
|
5
|
+
import { WorkSubject } from '@/infra/work-runner/constants'
|
|
6
|
+
import { E } from '@/shared/errors/defs'
|
|
7
|
+
import { jsonResponse, OpenApiTags } from '@/shared/network/openapi'
|
|
8
|
+
import { successJson } from '@/shared/network/utils'
|
|
9
|
+
import { generalValidator } from '@/shared/network/validator'
|
|
10
|
+
import { processExampleInputSchema } from './contracts'
|
|
11
|
+
|
|
12
|
+
export const exampleRoutes = new Hono<AppEnv>()
|
|
13
|
+
.get(
|
|
14
|
+
'/health',
|
|
15
|
+
describeRoute({
|
|
16
|
+
operationId: 'getHealth',
|
|
17
|
+
tags: [OpenApiTags.System],
|
|
18
|
+
summary: 'Health check',
|
|
19
|
+
responses: { 200: jsonResponse(v.object({ data: v.object({ status: v.string() }) })) },
|
|
20
|
+
}),
|
|
21
|
+
(c) => successJson(c, { status: 'ok' }),
|
|
22
|
+
)
|
|
23
|
+
.post(
|
|
24
|
+
'/examples/process',
|
|
25
|
+
describeRoute({
|
|
26
|
+
operationId: 'processExample',
|
|
27
|
+
tags: [OpenApiTags.Example],
|
|
28
|
+
summary: 'Process an example message in the worker pool',
|
|
29
|
+
responses: { 200: jsonResponse(v.object({ data: v.object({ normalized: v.string() }) })) },
|
|
30
|
+
}),
|
|
31
|
+
generalValidator('json', processExampleInputSchema),
|
|
32
|
+
async (c) => {
|
|
33
|
+
const input = c.req.valid('json')
|
|
34
|
+
const result = await c.get('exampleWorkRunner').run({
|
|
35
|
+
subject: WorkSubject.PROCESS_EXAMPLE,
|
|
36
|
+
input,
|
|
37
|
+
})
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
throw E.NORMAL.INTERNAL.create({
|
|
40
|
+
cause: result.error,
|
|
41
|
+
extensions: { workerCode: result.error.code },
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
return successJson(c, result.data)
|
|
45
|
+
},
|
|
46
|
+
)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MessageChannel } from 'node:worker_threads'
|
|
2
|
+
import { describe, expect, it } from 'vite-plus/test'
|
|
3
|
+
import { executeExampleTask } from './tasks'
|
|
4
|
+
|
|
5
|
+
describe('Example worker task', () => {
|
|
6
|
+
it('normalizes a message and reports progress', async () => {
|
|
7
|
+
const { port1, port2 } = new MessageChannel()
|
|
8
|
+
const messages: unknown[] = []
|
|
9
|
+
port1.on('message', (message) => messages.push(message))
|
|
10
|
+
const result = await executeExampleTask({ message: ' hello worker ' }, port2)
|
|
11
|
+
await new Promise<void>((resolve) => setImmediate(resolve))
|
|
12
|
+
|
|
13
|
+
expect(result).toEqual({
|
|
14
|
+
ok: true,
|
|
15
|
+
data: { original: ' hello worker ', normalized: 'hello worker', length: 12 },
|
|
16
|
+
})
|
|
17
|
+
expect(messages).toEqual(
|
|
18
|
+
expect.arrayContaining([
|
|
19
|
+
expect.objectContaining({ type: 'log' }),
|
|
20
|
+
expect.objectContaining({ type: 'progress', value: 100 }),
|
|
21
|
+
]),
|
|
22
|
+
)
|
|
23
|
+
port1.close()
|
|
24
|
+
port2.close()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('preserves a business exception model', async () => {
|
|
28
|
+
const { port1, port2 } = new MessageChannel()
|
|
29
|
+
await expect(executeExampleTask({ message: ' ' }, port2)).rejects.toMatchObject({
|
|
30
|
+
name: 'WorkerTaskBusinessError',
|
|
31
|
+
message: 'Message must not be empty',
|
|
32
|
+
details: { field: 'message' },
|
|
33
|
+
})
|
|
34
|
+
port1.close()
|
|
35
|
+
port2.close()
|
|
36
|
+
})
|
|
37
|
+
})
|