@simplysm/sd-claude 14.0.65 → 14.0.66
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/claude/references/sd-requirement-source-handling.md +64 -0
- package/claude/references/sd-simplysm14/README.md +44 -40
- package/claude/references/sd-simplysm14/apis/angular/README.md +95 -0
- package/claude/references/sd-simplysm14/apis/angular/app-structure.md +49 -0
- package/claude/references/sd-simplysm14/apis/angular/buttons.md +42 -0
- package/claude/references/sd-simplysm14/apis/angular/crud.md +35 -0
- package/claude/references/sd-simplysm14/apis/angular/forms.md +63 -0
- package/claude/references/sd-simplysm14/apis/angular/infrastructure.md +80 -0
- package/claude/references/sd-simplysm14/apis/angular/kanban.md +33 -0
- package/claude/references/sd-simplysm14/apis/angular/layout.md +41 -0
- package/claude/references/sd-simplysm14/apis/angular/modal.md +63 -0
- package/claude/references/sd-simplysm14/apis/angular/routing.md +45 -0
- package/claude/references/sd-simplysm14/apis/angular/select-dropdown.md +35 -0
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +50 -0
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +42 -0
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +52 -0
- package/claude/references/sd-simplysm14/apis/angular/toast.md +46 -0
- package/claude/references/sd-simplysm14/apis/angular/visual.md +41 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +76 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +83 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +80 -0
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +39 -0
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +112 -0
- package/claude/references/sd-simplysm14/apis/core-common/README.md +53 -0
- package/claude/references/sd-simplysm14/apis/core-common/extensions.md +123 -0
- package/claude/references/sd-simplysm14/apis/core-common/features.md +46 -0
- package/claude/references/sd-simplysm14/apis/core-common/types.md +114 -0
- package/claude/references/sd-simplysm14/apis/core-common/utils.md +158 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +12 -0
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +64 -0
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +52 -0
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +53 -0
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +81 -0
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +55 -0
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +111 -0
- package/claude/references/sd-simplysm14/apis/excel/README.md +81 -0
- package/claude/references/sd-simplysm14/apis/lint/README.md +80 -0
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +33 -0
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +77 -0
- package/claude/references/sd-simplysm14/apis/orm-common/executable.md +20 -0
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +92 -0
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +98 -0
- package/claude/references/sd-simplysm14/apis/orm-common/schema-builders.md +128 -0
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +59 -0
- package/claude/references/sd-simplysm14/apis/sd-claude/README.md +9 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +50 -0
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config.md +155 -0
- package/claude/references/sd-simplysm14/apis/service-client/README.md +92 -0
- package/claude/references/sd-simplysm14/apis/service-common/README.md +29 -0
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +63 -0
- package/claude/references/sd-simplysm14/apis/service-common/messages.md +56 -0
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +64 -0
- package/claude/references/sd-simplysm14/apis/service-common/service-types.md +43 -0
- package/claude/references/sd-simplysm14/apis/service-server/README.md +20 -0
- package/claude/references/sd-simplysm14/apis/service-server/auth.md +31 -0
- package/claude/references/sd-simplysm14/apis/service-server/builtin-services.md +47 -0
- package/claude/references/sd-simplysm14/apis/service-server/define-service.md +71 -0
- package/claude/references/sd-simplysm14/apis/service-server/internals.md +41 -0
- package/claude/references/sd-simplysm14/apis/service-server/server.md +66 -0
- package/claude/references/sd-simplysm14/apis/storage/README.md +69 -0
- package/claude/references/sd-simplysm14/{client-component.md → manuals/client-component.md} +133 -128
- package/claude/references/sd-simplysm14/manuals/client-crud.md +102 -0
- package/claude/references/sd-simplysm14/manuals/client-demo.md +128 -0
- package/claude/references/sd-simplysm14/manuals/client-rules.md +7 -0
- package/claude/references/sd-simplysm14/{client-setup.md → manuals/client-setup.md} +2 -2
- package/claude/references/sd-simplysm14/{client-tab.md → manuals/client-tab.md} +13 -11
- package/claude/references/sd-simplysm14/{orm-union.md → manuals/orm-union.md} +1 -1
- package/claude/references/sd-simplysm14/manuals/orm.md +75 -0
- package/claude/rules/sd-base-rules.md +172 -79
- package/claude/sd-check-bash.py +5 -0
- package/claude/sd-statusline.py +7 -12
- package/claude/skills/sd-commit/SKILL.md +7 -3
- package/claude/skills/sd-demo/SKILL.md +81 -62
- package/claude/skills/sd-demo/evals/fixtures/empty/.specs/260513120000_warehouse/spec.md +45 -0
- package/claude/skills/sd-demo/evals/fixtures/with-existing-screen/.specs/260513120000_warehouse/spec.md +42 -0
- package/claude/skills/sd-demo/evals/fixtures/with-existing-screen/packages/app/src/screens/dashboard/dashboard.view.ts +33 -0
- package/claude/skills/sd-demo/evals/fixtures/with-master-screen/.specs/260513120000_warehouse/spec.md +45 -0
- package/claude/skills/sd-demo/evals/fixtures/with-master-screen/packages/app/src/screens/dashboard/dashboard.view.ts +33 -0
- package/claude/skills/sd-demo/evals/fixtures/with-modal/.specs/260513120000_warehouse/spec.md +75 -0
- package/claude/skills/sd-demo/evals/fixtures/with-modal/packages/app/src/screens/dashboard/dashboard.view.ts +33 -0
- package/claude/skills/sd-demo/evals/fixtures/with-screens/.specs/260513120000_warehouse/spec.md +45 -0
- package/claude/skills/sd-demo/evals/fixtures/with-screens/packages/app/src/screens/dashboard/dashboard.view.ts +33 -0
- package/claude/skills/sd-demo/evals/golden.jsonl +5 -3
- package/claude/skills/sd-dev/SKILL.md +33 -63
- package/claude/skills/sd-dev/evals/fixtures/case-add/package.json +13 -0
- package/claude/skills/sd-dev/evals/fixtures/case-add/src/index.ts +10 -0
- package/claude/skills/sd-dev/evals/fixtures/case-add/tests/index.test.ts +11 -0
- package/claude/skills/sd-dev/evals/fixtures/case-add/tsconfig.json +12 -0
- package/claude/skills/sd-dev/evals/fixtures/case-bug/package.json +13 -0
- package/claude/skills/sd-dev/evals/fixtures/case-bug/src/index.ts +10 -0
- package/claude/skills/sd-dev/evals/fixtures/case-bug/tests/index.test.ts +11 -0
- package/claude/skills/sd-dev/evals/fixtures/case-bug/tsconfig.json +12 -0
- package/claude/skills/sd-dev/evals/fixtures/case-modify/package.json +13 -0
- package/claude/skills/sd-dev/evals/fixtures/case-modify/src/index.ts +10 -0
- package/claude/skills/sd-dev/evals/fixtures/case-modify/tests/index.test.ts +11 -0
- package/claude/skills/sd-dev/evals/fixtures/case-modify/tsconfig.json +12 -0
- package/claude/skills/sd-dev/evals/golden.jsonl +3 -3
- package/claude/skills/sd-docs/SKILL.md +53 -0
- package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +7 -0
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +5 -0
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +3 -0
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +6 -0
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +1 -0
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +5 -0
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +8 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +7 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +3 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +5 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +3 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +6 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +1 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +5 -0
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +8 -0
- package/claude/skills/sd-docs/evals/golden.jsonl +2 -0
- package/claude/skills/sd-docs/references/subagent-prompt.md +100 -0
- package/claude/skills/sd-impl/SKILL.md +125 -46
- package/claude/skills/sd-impl/evals/fixtures/case-new/.specs/260514120000_/352/261/260/353/236/230/354/262/230/spec.md +101 -0
- package/claude/skills/sd-impl/evals/fixtures/case-update/.specs/260514120000_/352/261/260/353/236/230/354/262/230/spec.md +101 -0
- package/claude/skills/sd-impl/evals/fixtures/case-update/src//352/261/260/353/236/230/354/262/230//352/261/260/353/236/230/354/262/230-/353/252/250/353/215/270.txt +1 -0
- package/claude/skills/sd-impl/evals/fixtures/case-update/src//352/261/260/353/236/230/354/262/230//352/261/260/353/236/230/354/262/230-/353/252/251/353/241/235.txt +1 -0
- package/claude/skills/sd-impl/evals/golden.jsonl +2 -3
- package/claude/skills/sd-skill/SKILL.md +3 -3
- package/claude/skills/sd-skill/evals/golden.jsonl +0 -1
- package/claude/skills/sd-skill/references/eval-authoring.md +15 -0
- package/claude/skills/sd-skill/references/eval-run.md +1 -1
- package/claude/skills/sd-skill/references/skill-authoring.md +8 -5
- package/claude/skills/sd-skill/scripts/run_eval.py +39 -60
- package/claude/skills/sd-spec/SKILL.md +163 -105
- package/claude/skills/sd-spec/references/example-spec.md +585 -0
- package/claude/skills/sd-spec/references/spec-authoring.md +287 -0
- package/claude/skills/sd-spec/references/spec-md-template.md +15 -93
- package/claude/skills/sd-unpack/SKILL.md +7 -1
- package/claude/skills/sd-unpack/scripts/handlers/_common.py +10 -0
- package/claude/skills/sd-unpack/scripts/handlers/eml_handler.py +5 -13
- package/claude/skills/sd-unpack/scripts/handlers/msg_handler.py +3 -12
- package/claude/skills/sd-unpack/scripts/handlers/office_com.py +23 -37
- package/claude/skills/sd-unpack/scripts/handlers/office_worker.py +1 -4
- package/claude/skills/sd-unpack/scripts/handlers/pdf_handler.py +4 -13
- package/claude/skills/sd-unpack/scripts/unpack.py +4 -4
- package/claude/skills/sd-use/SKILL.md +1 -0
- package/claude/skills/sd-wip/SKILL.md +38 -0
- package/claude/skills/sd-wip/evals/fixtures/with-artifact/projects/acct/_wip.md +3 -0
- package/claude/skills/sd-wip/evals/fixtures/with-artifact/projects/acct/spec.md +15 -0
- package/claude/skills/sd-wip/evals/fixtures/with-existing-wip/.wips/260101120000_acct.md +6 -0
- package/claude/skills/sd-wip/evals/fixtures/with-existing-wip-for-compact/.wips/260101120000_acct.md +14 -0
- package/claude/skills/sd-wip/evals/golden.jsonl +4 -0
- package/claude/skills/sd-wip/references/compact.md +79 -0
- package/package.json +1 -1
- package/claude/references/sd-simplysm14/orm.md +0 -11
- package/claude/skills/sd-demo/evals/fixtures/basic-single-req/.specs/260503143025/REQ-001-/354/236/205/352/263/240/354/247/200/354/213/234/354/204/234/352/270/264/352/270/211/355/221/234/354/213/234/spec.md +0 -27
- package/claude/skills/sd-demo/evals/fixtures/basic-single-req/.specs/260503143025/overview.md +0 -12
- package/claude/skills/sd-demo/evals/fixtures/basic-single-req/src/components/Button.tsx +0 -12
- package/claude/skills/sd-demo/evals/fixtures/basic-single-req/src/components/Input.tsx +0 -27
- package/claude/skills/sd-demo/evals/fixtures/mock-data-policy/.specs/260503143025/REQ-001-/352/261/260/353/236/230/354/262/230/353/252/251/353/241/235/355/231/224/353/251/264/spec.md +0 -25
- package/claude/skills/sd-demo/evals/fixtures/mock-data-policy/.specs/260503143025/overview.md +0 -12
- package/claude/skills/sd-demo/evals/fixtures/mock-data-policy/src/components/Input.tsx +0 -25
- package/claude/skills/sd-demo/evals/fixtures/mock-data-policy/src/pages/.gitkeep +0 -0
- package/claude/skills/sd-demo/evals/fixtures/multi-req-domain/.specs/260503143025/REQ-001-RTP/354/236/205/353/240/245/355/231/224/353/251/264/spec.md +0 -19
- package/claude/skills/sd-demo/evals/fixtures/multi-req-domain/.specs/260503143025/REQ-002-RTP/354/266/234/353/240/245/355/231/224/353/251/264/spec.md +0 -20
- package/claude/skills/sd-demo/evals/fixtures/multi-req-domain/.specs/260503143025/overview.md +0 -16
- package/claude/skills/sd-demo/evals/fixtures/multi-req-domain/src/components/Button.tsx +0 -6
- package/claude/skills/sd-demo/evals/fixtures/multi-req-domain/src/components/Input.tsx +0 -15
- package/claude/skills/sd-demo/evals/fixtures/multi-req-domain/src/pages/.gitkeep +0 -0
- package/claude/skills/sd-demo/references/demo-md-template.md +0 -92
- package/claude/skills/sd-dev/evals/fixtures/multi-req-stop-after-spec/.docs/20260301_/352/263/240/352/260/235/354/202/254_/354/236/205/352/263/240/354/232/224/354/262/255.eml +0 -5
- package/claude/skills/sd-dev/evals/fixtures/multi-req-stop-after-spec/.docs/20260305_/352/263/240/352/260/235/354/202/254_/354/266/234/352/263/240/354/232/224/354/262/255.eml +0 -6
- package/claude/skills/sd-dev/evals/fixtures/multi-req-stop-after-spec/.docs/20260310_/352/263/240/352/260/235/354/202/254_/352/266/214/355/225/234.eml +0 -6
- package/claude/skills/sd-dev/evals/fixtures/single-req-auto-chain/src/lib/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/start-from-plan/.specs/260503143025/REQ-001-/355/225/240/354/235/270/352/263/204/354/202/260/spec.md +0 -12
- package/claude/skills/sd-dev/evals/fixtures/start-from-plan/src/lib/.gitkeep +0 -0
- package/claude/skills/sd-impl/evals/fixtures/basic-single-r/.specs/260503143025/REQ-001-/354/236/205/352/263/240/354/247/200/354/213/234/354/204/234/352/270/264/352/270/211/355/221/234/354/213/234/plan.md +0 -28
- package/claude/skills/sd-impl/evals/fixtures/basic-single-r/.specs/260503143025/REQ-001-/354/236/205/352/263/240/354/247/200/354/213/234/354/204/234/352/270/264/352/270/211/355/221/234/354/213/234/spec.md +0 -14
- package/claude/skills/sd-impl/evals/fixtures/basic-single-r/src/components/Input.tsx +0 -6
- package/claude/skills/sd-impl/evals/fixtures/basic-single-r/src/pages/.gitkeep +0 -0
- package/claude/skills/sd-impl/evals/fixtures/multi-r/.specs/260503143025/REQ-001-/352/261/260/353/236/230/354/262/230/353/252/251/353/241/235/plan.md +0 -40
- package/claude/skills/sd-impl/evals/fixtures/multi-r/.specs/260503143025/REQ-001-/352/261/260/353/236/230/354/262/230/353/252/251/353/241/235/spec.md +0 -13
- package/claude/skills/sd-impl/evals/fixtures/multi-r/src/components/Input.tsx +0 -25
- package/claude/skills/sd-impl/evals/fixtures/multi-r/src/pages/.gitkeep +0 -0
- package/claude/skills/sd-impl/evals/fixtures/with-test-file/.specs/260503143025/REQ-001-/355/225/240/354/235/270/352/263/204/354/202/260/plan.md +0 -26
- package/claude/skills/sd-impl/evals/fixtures/with-test-file/.specs/260503143025/REQ-001-/355/225/240/354/235/270/352/263/204/354/202/260/spec.md +0 -12
- package/claude/skills/sd-impl/evals/fixtures/with-test-file/src/lib/.gitkeep +0 -0
- package/claude/skills/sd-impl/references/impl-md-template.md +0 -87
- package/claude/skills/sd-impl/references/modes-and-failure.md +0 -65
- package/claude/skills/sd-plan/SKILL.md +0 -130
- package/claude/skills/sd-plan/evals/fixtures/already-implemented/.specs/260503143025/REQ-001-/354/202/254/354/232/251/354/236/220/353/252/251/353/241/235/spec.md +0 -14
- package/claude/skills/sd-plan/evals/fixtures/already-implemented/src/api/user.ts +0 -13
- package/claude/skills/sd-plan/evals/fixtures/already-implemented/src/components/Input.tsx +0 -15
- package/claude/skills/sd-plan/evals/fixtures/already-implemented/src/pages/UserList.tsx +0 -29
- package/claude/skills/sd-plan/evals/fixtures/basic-greenfield/.specs/260503143025/REQ-001-/354/236/205/352/263/240/354/247/200/354/213/234/354/204/234/352/270/264/352/270/211/355/221/234/354/213/234/spec.md +0 -17
- package/claude/skills/sd-plan/evals/fixtures/basic-greenfield/src/components/Input.tsx +0 -6
- package/claude/skills/sd-plan/evals/fixtures/basic-greenfield/src/pages/.gitkeep +0 -0
- package/claude/skills/sd-plan/evals/fixtures/demo-built/.specs/260503143025/DEMO-001-/352/261/260/353/236/230/354/262/230/demo.md +0 -26
- package/claude/skills/sd-plan/evals/fixtures/demo-built/.specs/260503143025/REQ-001-/352/261/260/353/236/230/354/262/230/353/252/251/353/241/235/355/231/224/353/251/264/spec.md +0 -15
- package/claude/skills/sd-plan/evals/fixtures/demo-built/src/components/Input.tsx +0 -25
- package/claude/skills/sd-plan/evals/fixtures/demo-built/src/data/mock-customer.ts +0 -16
- package/claude/skills/sd-plan/evals/fixtures/demo-built/src/pages/CustomerList.tsx +0 -25
- package/claude/skills/sd-plan/evals/golden.jsonl +0 -3
- package/claude/skills/sd-plan/references/plan-md-template.md +0 -138
- package/claude/skills/sd-spec/evals/fixtures/bulk-multi-source/.docs/20260122_/352/263/240/352/260/235/354/202/254_/354/236/205/352/263/240/354/247/200/354/213/234/354/204/234/352/260/234/354/204/240/354/232/224/354/262/255.eml +0 -20
- package/claude/skills/sd-spec/evals/fixtures/bulk-multi-source/.docs/20260205_/352/263/240/352/260/235/354/202/254_/354/266/234/352/263/240/355/231/224/353/251/264/352/264/200/353/240/250.eml +0 -17
- package/claude/skills/sd-spec/evals/fixtures/bulk-multi-source/.docs/20260301_/352/263/240/352/260/235/354/202/254_/352/266/214/355/225/234/354/262/264/352/263/204.eml +0 -18
- package/claude/skills/sd-spec/evals/fixtures/conflict-detection/.docs/20260317_/352/263/240/352/260/235/354/202/254_/352/270/264/352/270/211/354/262/230/353/246/254/353/260/251/354/271/250.eml +0 -17
- package/claude/skills/sd-spec/evals/fixtures/conflict-detection/.docs//355/232/214/354/235/230/353/214/200/353/263/270_20260320.txt +0 -13
- package/claude/skills/sd-spec/evals/fixtures/direct-simple/.gitkeep +0 -0
- package/claude/skills/sd-spec/evals/fixtures/spec-md-input/.specs/260101120000/REQ-001-test/spec.md +0 -19
- package/claude/skills/sd-spec/evals/fixtures/spec-md-input/.specs/260101120000/overview.md +0 -18
- package/claude/skills/sd-spec/evals/golden.jsonl +0 -4
- package/claude/skills/sd-spec/references/overview-md-template.md +0 -89
- package/claude/skills/sd-spec/references/raw-input-handling.md +0 -96
- package/claude/skills/sd-verify/SKILL.md +0 -96
- package/claude/skills/sd-verify/evals/fixtures/all-satisfied/.specs/260503143025/REQ-001-/355/225/240/354/235/270/352/263/204/354/202/260/impl.md +0 -20
- package/claude/skills/sd-verify/evals/fixtures/all-satisfied/.specs/260503143025/REQ-001-/355/225/240/354/235/270/352/263/204/354/202/260/plan.md +0 -14
- package/claude/skills/sd-verify/evals/fixtures/all-satisfied/.specs/260503143025/REQ-001-/355/225/240/354/235/270/352/263/204/354/202/260/spec.md +0 -12
- package/claude/skills/sd-verify/evals/fixtures/all-satisfied/src/lib/discount.test.ts +0 -11
- package/claude/skills/sd-verify/evals/fixtures/all-satisfied/src/lib/discount.ts +0 -7
- package/claude/skills/sd-verify/evals/fixtures/partial-mismatch/.specs/260503143025/REQ-001-/354/202/254/354/232/251/354/236/220/353/252/251/353/241/235/impl.md +0 -21
- package/claude/skills/sd-verify/evals/fixtures/partial-mismatch/.specs/260503143025/REQ-001-/354/202/254/354/232/251/354/236/220/353/252/251/353/241/235/plan.md +0 -21
- package/claude/skills/sd-verify/evals/fixtures/partial-mismatch/.specs/260503143025/REQ-001-/354/202/254/354/232/251/354/236/220/353/252/251/353/241/235/spec.md +0 -12
- package/claude/skills/sd-verify/evals/fixtures/partial-mismatch/src/pages/UserList.tsx +0 -19
- package/claude/skills/sd-verify/evals/fixtures/tdd-only/.specs/260503143025/REQ-001-/352/270/210/354/225/241/352/262/200/354/246/235/impl.md +0 -19
- package/claude/skills/sd-verify/evals/fixtures/tdd-only/.specs/260503143025/REQ-001-/352/270/210/354/225/241/352/262/200/354/246/235/plan.md +0 -14
- package/claude/skills/sd-verify/evals/fixtures/tdd-only/.specs/260503143025/REQ-001-/352/270/210/354/225/241/352/262/200/354/246/235/spec.md +0 -12
- package/claude/skills/sd-verify/evals/fixtures/tdd-only/src/lib/validate-amount.test.ts +0 -10
- package/claude/skills/sd-verify/evals/fixtures/tdd-only/src/lib/validate-amount.ts +0 -7
- package/claude/skills/sd-verify/evals/golden.jsonl +0 -3
- package/claude/skills/sd-verify/references/verify-md-template.md +0 -99
- /package/claude/skills/{sd-demo/evals/fixtures/basic-single-req/src/pages → sd-wip/evals/fixtures/empty}/.gitkeep +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# @simplysm/sd-cli — sd-config
|
|
2
|
+
|
|
3
|
+
`sd.config.ts` 작성용 타입 모음. 진실 근거: `packages/sd-cli/src/sd-config.types.ts`.
|
|
4
|
+
|
|
5
|
+
## 진입 타입
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import type { SdConfig, SdConfigFn, SdConfigParams } from "@simplysm/sd-cli";
|
|
9
|
+
|
|
10
|
+
const config: SdConfigFn = (params) => ({
|
|
11
|
+
packages: {
|
|
12
|
+
"core-common": { target: "neutral" },
|
|
13
|
+
"core-node": { target: "node" },
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
export default config;
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- `SdConfigFn = (params: SdConfigParams) => SdConfig | Promise<SdConfig>`
|
|
20
|
+
- `SdConfigParams = { cwd: string; dev: boolean; opt: string[] }` — `opt` 는 CLI `-o` 플래그.
|
|
21
|
+
- `SdConfig = { packages: Record<string, SdPackageConfig | undefined>; replaceDeps?: Record<string, string>; postPublish?: SdPostPublishScriptConfig[] }`
|
|
22
|
+
- `packages` 키 = `packages/` 하위 디렉토리명.
|
|
23
|
+
- `replaceDeps`: node_modules 의 패키지를 로컬 소스로 심링크 교체. 예: `{ "@simplysm/*": "../simplysm/packages/*" }` (key 의 `*` 가 value 의 `*` 로 치환).
|
|
24
|
+
|
|
25
|
+
## 패키지 설정
|
|
26
|
+
|
|
27
|
+
`SdPackageConfig = SdBuildPackageConfig | SdClientPackageConfig | SdServerPackageConfig | SdScriptsPackageConfig`.
|
|
28
|
+
|
|
29
|
+
### SdBuildPackageConfig (라이브러리: node/browser/neutral)
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
{
|
|
33
|
+
target: "node" | "browser" | "neutral"; // BuildTarget
|
|
34
|
+
publish?: SdPublishConfig;
|
|
35
|
+
copySrc?: string[]; // src/ 기준 glob → dist/ 로 복사
|
|
36
|
+
watch?: SdWatchHookConfig; // watch 모드 훅
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### SdClientPackageConfig (Frontend 앱)
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
{
|
|
44
|
+
target: "client";
|
|
45
|
+
server: string | number; // 연결 서버 패키지명, 또는 포트
|
|
46
|
+
env?: Record<string, string>; // esbuild define 으로 process.env 치환
|
|
47
|
+
publish?: SdPublishConfig;
|
|
48
|
+
capacitor?: SdCapacitorConfig;
|
|
49
|
+
electron?: SdElectronConfig;
|
|
50
|
+
configs?: Record<string, unknown>; // 런타임 설정 → dist/.config.json
|
|
51
|
+
exclude?: string[]; // Capacitor/Electron package.json 에 추가
|
|
52
|
+
browserSupport?: SdBrowserSupportConfig;
|
|
53
|
+
pwa?: false | SdPwaConfig; // 미지정 시 기본값으로 활성
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### SdServerPackageConfig (Fastify 서버)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
{
|
|
61
|
+
target: "server";
|
|
62
|
+
env?: Record<string, string>; // esbuild banner 로 process.env.KEY 상수 치환
|
|
63
|
+
publish?: SdPublishConfig;
|
|
64
|
+
configs?: Record<string, unknown>; // 런타임 설정 → dist/.config.json
|
|
65
|
+
externals?: string[]; // esbuild external (binding.gyp 자동 감지에 추가)
|
|
66
|
+
pm2?: { name?: string; ignoreWatchPaths?: string[] }; // 지정 시 dist/pm2.config.cjs 생성
|
|
67
|
+
packageManager?: "volta" | "mise"; // mise.toml / volta 설정 생성에 영향
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### SdScriptsPackageConfig (유틸·임의 명령 실행)
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
{
|
|
75
|
+
target: "scripts";
|
|
76
|
+
publish?: SdPublishConfig;
|
|
77
|
+
watch?: SdWatchHookConfig; // 미지정이면 watch/typecheck 에서 제외
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`SdWatchHookConfig`: `{ target: string[]; cmd: string; args?: string[] }`. `target` 은 패키지 디렉토리 기준 glob. 매칭 변경 시 `cmd args` 실행.
|
|
82
|
+
|
|
83
|
+
## 배포 설정
|
|
84
|
+
|
|
85
|
+
`SdPublishConfig`:
|
|
86
|
+
- `{ type: "npm" }` — npm 레지스트리.
|
|
87
|
+
- `{ type: "local-directory"; path: string }` — 로컬 복사. `path` 에 `%VER%`, `%PROJECT%` 치환.
|
|
88
|
+
- `{ type: "ftp" | "ftps" | "sftp"; host; port?; path?; user?; password? }`.
|
|
89
|
+
|
|
90
|
+
`postPublish` 항목 `SdPostPublishScriptConfig`: `{ type: "script"; cmd: string; args: string[] }`. `args` 의 `%VER%`, `%PROJECT%` 치환.
|
|
91
|
+
|
|
92
|
+
## 클라이언트 부속 옵션
|
|
93
|
+
|
|
94
|
+
### SdCapacitorConfig
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
{
|
|
98
|
+
appId: string; // 예 "com.example.app"
|
|
99
|
+
appName: string;
|
|
100
|
+
plugins?: Record<string, Record<string, unknown> | true>;
|
|
101
|
+
icon?: string; // 패키지 기준 상대 경로
|
|
102
|
+
debug?: boolean;
|
|
103
|
+
platform?: { android?: SdCapacitorAndroidConfig };
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
`SdCapacitorAndroidConfig`: `config`(AndroidManifest application 속성), `bundle`(true=AAB, false=APK), `intentFilters`, `sign: SdCapacitorSignConfig`, `sdkVersion`, `permissions: SdCapacitorPermission[]`.
|
|
108
|
+
|
|
109
|
+
- `SdCapacitorSignConfig`: `keystore, storePassword, alias, password, keystoreType?` (기본 `"jks"`).
|
|
110
|
+
- `SdCapacitorPermission`: `{ name; maxSdkVersion?; ignore? }`.
|
|
111
|
+
- `SdCapacitorIntentFilter`: `{ action?; category? }`.
|
|
112
|
+
|
|
113
|
+
### SdElectronConfig
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
{
|
|
117
|
+
appId: string;
|
|
118
|
+
portable?: boolean; // true=포터블 exe, 미지정/false=NSIS 설치본
|
|
119
|
+
installerIcon?: string; // .ico, 패키지 기준 상대 경로
|
|
120
|
+
reinstallDependencies?: string[]; // 네이티브 모듈 등
|
|
121
|
+
postInstallScript?: string;
|
|
122
|
+
nsisOptions?: Record<string, unknown>;
|
|
123
|
+
env?: Record<string, string>; // electron-main.ts 의 process.env
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### SdPwaConfig
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
{
|
|
131
|
+
manifest?: {
|
|
132
|
+
name?; short_name?;
|
|
133
|
+
display?: "standalone" | "fullscreen" | "minimal-ui" | "browser";
|
|
134
|
+
theme_color?; background_color?;
|
|
135
|
+
icons?: Array<{ src: string; sizes: string; type?: string }>;
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`pwa: false` 로 비활성. 미지정 시 기본값으로 활성.
|
|
141
|
+
|
|
142
|
+
### SdBrowserSupportConfig
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
{
|
|
146
|
+
browserslist?: string | string[]; // 예 "last 2 Chrome versions"
|
|
147
|
+
postCss?: { plugins: [string, (object | string)?][] };
|
|
148
|
+
legacyModule?: boolean; // 코드 분할 비활성 + import.meta 치환
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## 부수 타입
|
|
153
|
+
|
|
154
|
+
- `BuildTarget = "node" | "browser" | "neutral"` — `SdBuildPackageConfig.target`.
|
|
155
|
+
- `NpmConfig` — `package.json` 구조 헬퍼 (`name`, `version`, `dependencies`, `devDependencies`, `peerDependencies`, `volta?`).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @simplysm/service-client
|
|
2
|
+
|
|
3
|
+
`@simplysm/service-server` 와 WebSocket 으로 통신하는 클라이언트. RPC 호출·이벤트 구독·파일 업/다운로드·원격 ORM 실행을 단일 `ServiceClient` 에서 제공한다 (Node/브라우저 공용).
|
|
4
|
+
|
|
5
|
+
## 사용 트리거 인덱스
|
|
6
|
+
- **createServiceClient / ServiceConnectionOptions** — 클라이언트 인스턴스 생성·서버 접속/종료할 때.
|
|
7
|
+
- **getService / send / ServiceProxy** — 서버 서비스 메서드를 타입 안전 RPC 로 호출할 때.
|
|
8
|
+
- **auth** — 서버 인증 토큰을 등록하고 재연결 시 자동 재인증되게 할 때.
|
|
9
|
+
- **getEvent / addListener / removeListener / emitEvent** — 서버 사이드 이벤트 구독 또는 다른 클라이언트로 이벤트 발행할 때.
|
|
10
|
+
- **uploadFile / downloadFileBuffer** — 서버에 파일을 올리거나 정적 경로에서 바이트로 받을 때.
|
|
11
|
+
- **createOrmClientConnector / OrmConnectOptions** — 서버의 ORM 서비스를 통해 DbContext 트랜잭션을 원격 실행할 때.
|
|
12
|
+
- **ServiceClient 이벤트 (`state`, `request-progress`, `response-progress`, `server-progress`)** — 연결 상태와 전송/응답/서버 진행률을 구독할 때.
|
|
13
|
+
|
|
14
|
+
## 연결/생성
|
|
15
|
+
```ts
|
|
16
|
+
createServiceClient(name: string, options: ServiceConnectionOptions): ServiceClient
|
|
17
|
+
interface ServiceConnectionOptions { port: number; host: string; ssl?: boolean; maxReconnectCount?: number; }
|
|
18
|
+
client.connect(): Promise<void>
|
|
19
|
+
client.close(): Promise<void>
|
|
20
|
+
client.connected: boolean
|
|
21
|
+
client.hostUrl: string // `${ssl?https:http}://host:port`
|
|
22
|
+
```
|
|
23
|
+
- WebSocket URL 은 `${ws|wss}://host:port/ws` 로 고정.
|
|
24
|
+
- `maxReconnectCount` 기본 10, `0` 이면 재연결 비활성.
|
|
25
|
+
- 5s ping / 30s 무응답 시 강제 재연결, 재연결 성공 시 인증 토큰과 이벤트 리스너가 자동 복구됨.
|
|
26
|
+
|
|
27
|
+
## RPC 호출
|
|
28
|
+
```ts
|
|
29
|
+
client.getService<TService>(serviceName): ServiceProxy<TService>
|
|
30
|
+
client.send(serviceName, methodName, params, progress?): Promise<unknown>
|
|
31
|
+
type ServiceProxy<T> = { [K in keyof T]: T[K] extends (...a:infer P)=>infer R ? (...a:P)=>Promise<Awaited<R>> : never }
|
|
32
|
+
interface ServiceProgress { request?(s); response?(s); server?(s); }
|
|
33
|
+
interface ServiceProgressState { uuid: string; totalSize: number; completedSize: number; }
|
|
34
|
+
```
|
|
35
|
+
- `getService` 는 Proxy 라 임의 메서드명 호출 가능. 타입은 `TService` 로만 보장됨.
|
|
36
|
+
- 메시지 이름은 `"<serviceName>.<methodName>"` 으로 전송.
|
|
37
|
+
- 30KB 초과 또는 `Uint8Array`/큰 배열 페이로드는 Worker 에서 인코딩/디코딩.
|
|
38
|
+
|
|
39
|
+
## 인증
|
|
40
|
+
```ts
|
|
41
|
+
client.auth(token: string): Promise<void>
|
|
42
|
+
```
|
|
43
|
+
- 서버에 `auth` 메시지를 보내고 토큰을 내부 저장. 재연결 시 자동으로 같은 토큰으로 재인증.
|
|
44
|
+
- `uploadFile` 은 인증 토큰이 없으면 에러를 던짐.
|
|
45
|
+
|
|
46
|
+
## 이벤트 (서버 push)
|
|
47
|
+
```ts
|
|
48
|
+
client.getEvent<TEventDef>(eventName): ClientEventProxy<TEventDef>
|
|
49
|
+
client.addListener<TEventDef>(eventName, info, cb): Promise<string> // key 반환
|
|
50
|
+
client.removeListener(key): Promise<void>
|
|
51
|
+
client.emitEvent<TEventDef>(eventName, infoSelector, data): Promise<void>
|
|
52
|
+
interface ClientEventProxy<T> { addListener(info, cb): Promise<string>; removeListener(key): Promise<void>; emit(infoSelector, data): Promise<void>; }
|
|
53
|
+
```
|
|
54
|
+
- `addListener` 는 연결 상태에서만 가능. 로컬 맵에도 보관해 재연결 시 자동 재구독.
|
|
55
|
+
- `emitEvent` 는 서버에 `evt:gets` 로 후보를 받아 `infoSelector` 통과분에만 `evt:emit` 호출.
|
|
56
|
+
- 핸들러 내부 에러는 로깅만 되고 다른 핸들러 호출은 계속됨.
|
|
57
|
+
|
|
58
|
+
## 파일 업/다운로드
|
|
59
|
+
```ts
|
|
60
|
+
client.uploadFile(files: File[] | FileCollection | { name: string; data: BlobInput }[]): Promise<ServiceUploadResult[]>
|
|
61
|
+
client.downloadFileBuffer(relPath: string): Promise<Uint8Array>
|
|
62
|
+
type BlobInput = Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | string
|
|
63
|
+
interface FileCollection { length; item(i); [i]; [Symbol.iterator]() } // 브라우저 FileList 호환
|
|
64
|
+
```
|
|
65
|
+
- 업로드: `POST {hostUrl}/upload` (multipart). 헤더 `x-sd-client-name`, `Authorization: Bearer <token>`. 인증 필수.
|
|
66
|
+
- 다운로드: `GET {hostUrl}/{relPath}` 의 응답 본문을 `Uint8Array` 로 반환.
|
|
67
|
+
- 브라우저 전용 `File`/`FileList` 대신 `BlobInput`/`FileCollection` 으로 Node 환경도 지원.
|
|
68
|
+
|
|
69
|
+
## ORM 클라이언트
|
|
70
|
+
```ts
|
|
71
|
+
createOrmClientConnector(client: ServiceClient): OrmClientConnector
|
|
72
|
+
interface OrmConnectOptions<T extends DbContext> {
|
|
73
|
+
DbClass: new (executor, { database, schema? }) => T;
|
|
74
|
+
connOpt: DbConnOptions & { configName: string };
|
|
75
|
+
dbContextOpt?: { database: string; schema: string };
|
|
76
|
+
}
|
|
77
|
+
connector.connect(config, async db => ...) // 트랜잭션 래핑
|
|
78
|
+
connector.connectWithoutTransaction(config, async db => ...)
|
|
79
|
+
```
|
|
80
|
+
- 서버의 `"Orm"` 서비스에 RPC 를 걸어 `getInfo` → `connect` → `executeDefs` 등을 실행.
|
|
81
|
+
- `dbContextOpt` 생략 시 서버가 알려준 `database`/`schema` 사용. 둘 다 비면 에러.
|
|
82
|
+
- 외래키 참조 위반 에러(`a parent row: a foreign key constraint` / `conflicted with the REFERENCE`)는 사용자 메시지로 변환되어 throw.
|
|
83
|
+
|
|
84
|
+
## 상태/진행률 이벤트
|
|
85
|
+
```ts
|
|
86
|
+
client.on("state", (s: "connected" | "closed" | "reconnecting") => ...)
|
|
87
|
+
client.on("request-progress", (s: ServiceProgressState) => ...) // 클라이언트 → 서버 전송 청크 진행
|
|
88
|
+
client.on("response-progress", (s: ServiceProgressState) => ...) // 서버 → 클라이언트 응답 청크 진행
|
|
89
|
+
client.on("server-progress", (s: ServiceProgressState) => ...) // 서버가 명시적으로 보내는 진행률
|
|
90
|
+
```
|
|
91
|
+
- 단일 청크 메시지에는 progress 가 발행되지 않음(분할 메시지에서만).
|
|
92
|
+
- `send` 호출 시 전달한 `progress?: ServiceProgress` 콜백은 인스턴스 이벤트와 별도로 호출됨.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @simplysm/service-common
|
|
2
|
+
|
|
3
|
+
서버/클라이언트가 공유하는 서비스 프로토콜·메시지 타입·서비스 인터페이스·앱 구조 정의·이벤트 정의 유틸.
|
|
4
|
+
|
|
5
|
+
## 사용 트리거 인덱스
|
|
6
|
+
|
|
7
|
+
- **`createServiceProtocol` / `ServiceProtocol` / `ServiceMessageDecodeResult` / `PROTOCOL_CONFIG`** — 서버/클라이언트 transport 의 바이너리 V2 프로토콜(헤더 28B + JSON, 3MB↑ 자동 청킹, 100MB 한계) 인/디코더 생성. 자세히: [protocol.md](./protocol.md)
|
|
8
|
+
- **메시지 타입 (`ServiceMessage` / `ServiceClientMessage` / `ServiceServerMessage` / `ServiceServerRawMessage` 및 9 종의 개별 메시지)** — 프로토콜 위에 실리는 메시지 식별·타입 가드·핸들러 분기. 자세히: [messages.md](./messages.md)
|
|
9
|
+
- **서비스 인터페이스 (`OrmService` / `AutoUpdateService` / `AppStructureService` / `DbConnOptions`)** — 서버 구현·클라이언트 호출 양쪽이 공유하는 빌트인 서비스의 시그니처. 자세히: [service-types.md](./service-types.md)
|
|
10
|
+
- **앱 구조 (`AppStructureItem` / `AppStructureGroupItem` / `AppStructureLeafItem` / `AppStructureSubPermission` / `FlatPermission` + `isUsableModules` / `isUsableModulesChain` / `getFlatPermissions`)** — 메뉴/권한 트리 정의와 모듈 기반 가용성 평가·평탄화. 자세히: [app-structure.md](./app-structure.md)
|
|
11
|
+
- **`defineEvent` / `ServiceEventDef`** — 서버 발신·클라이언트 구독에서 `info`/`data` 타입을 공유하기 위한 이벤트 정의.
|
|
12
|
+
- **`ServiceUploadResult`** — 파일 업로드 결과(저장 경로·원본명·바이트 크기).
|
|
13
|
+
|
|
14
|
+
## defineEvent / ServiceEventDef
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
function defineEvent<TInfo, TData>(eventName: string): ServiceEventDef<TInfo, TData>
|
|
18
|
+
interface ServiceEventDef<TInfo, TData> { eventName: string; readonly $info: TInfo; readonly $data: TData; }
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`$info`/`$data` 는 런타임 미사용, 타입 추출 전용 마커. 서버는 `defineEvent` 로 정의·export 하고, 클라이언트는 `import type` 으로 가져와 `addListener<typeof Evt>(...)` / `emitEvent<typeof Evt>(...)` 에 전달해 `info` 필터·`data` 페이로드를 정적으로 검증한다.
|
|
22
|
+
|
|
23
|
+
## ServiceUploadResult
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
interface ServiceUploadResult { path: string; filename: string; size: number; }
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
서버 업로드 핸들러가 클라이언트로 반환하는 결과. `path` 는 서버 내부 저장 경로다.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# @simplysm/service-common — app-structure
|
|
2
|
+
|
|
3
|
+
메뉴/권한 트리 정의 타입과 모듈(라이선스/플랜 등) 기반 가용성 평가·평탄화 함수.
|
|
4
|
+
|
|
5
|
+
## 타입
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
type AppStructureItem<TModule = unknown> =
|
|
9
|
+
| AppStructureGroupItem<TModule>
|
|
10
|
+
| AppStructureLeafItem<TModule>;
|
|
11
|
+
|
|
12
|
+
interface AppStructureGroupItem<TModule> {
|
|
13
|
+
code: string; title: string;
|
|
14
|
+
modules?: TModule[]; requiredModules?: TModule[];
|
|
15
|
+
icon?: string;
|
|
16
|
+
children: AppStructureItem<TModule>[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface AppStructureLeafItem<TModule> {
|
|
20
|
+
code: string; title: string;
|
|
21
|
+
modules?: TModule[]; requiredModules?: TModule[];
|
|
22
|
+
perms?: ("use" | "edit")[];
|
|
23
|
+
subPerms?: AppStructureSubPermission<TModule>[];
|
|
24
|
+
icon?: string; url?: string;
|
|
25
|
+
isNotMenu?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface AppStructureSubPermission<TModule> {
|
|
29
|
+
code: string; title: string;
|
|
30
|
+
modules?: TModule[]; requiredModules?: TModule[];
|
|
31
|
+
perms: ("use" | "edit")[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface FlatPermission<TModule = unknown> {
|
|
35
|
+
titleChain: string[];
|
|
36
|
+
codeChain: string[]; // [...상위 code, perm] 또는 [...상위 code, subPerm.code, perm]
|
|
37
|
+
modulesChain: TModule[][];
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Group/Leaf 는 `children` 존재로 구분(`"children" in item`).
|
|
42
|
+
|
|
43
|
+
## isUsableModules(modules, requiredModules, usableModules) → boolean
|
|
44
|
+
|
|
45
|
+
- `requiredModules` (AND): 모두 `usableModules` 에 포함되어야 함. 없거나 빈 배열이면 통과.
|
|
46
|
+
- `modules` (OR): 비었거나 그 중 하나라도 `usableModules` 에 있으면 통과.
|
|
47
|
+
|
|
48
|
+
## isUsableModulesChain(modulesChain, requiredModulesChain, usableModules) → boolean
|
|
49
|
+
|
|
50
|
+
체인 각 레벨에 대해 `modules` OR 와 `requiredModules` AND 모두 만족해야 통과. 트리 깊이별 누적 조건 평가용.
|
|
51
|
+
|
|
52
|
+
## getFlatPermissions(items, usableModules) → FlatPermission[]
|
|
53
|
+
|
|
54
|
+
트리를 BFS 로 순회하며 각 leaf 의 `perms` / `subPerms` 를 평탄화한다.
|
|
55
|
+
|
|
56
|
+
- 진행 중 노드 단위로 `isUsableModulesChain` 체크 — 실패 시 하위 폐기.
|
|
57
|
+
- `subPerms` 도 자체 `modules`/`requiredModules` 로 추가 필터.
|
|
58
|
+
- 결과 `codeChain` 마지막에 권한값(`"use"|"edit"`)이 붙는다.
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
const perms = getFlatPermissions(items, ["BASIC", "PRO"]);
|
|
62
|
+
// [{ titleChain: ["주문","주문등록"], codeChain: ["order","register","use"], modulesChain: [["PRO"]] }, ...]
|
|
63
|
+
```
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# @simplysm/service-common — messages
|
|
2
|
+
|
|
3
|
+
`ServiceProtocol` 위에 실리는 메시지 식별자(`name`) 별 바디 스키마. 모든 인터페이스는 `name` discriminator 로 좁힌다.
|
|
4
|
+
|
|
5
|
+
## 유니언
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
type ServiceMessage =
|
|
9
|
+
| ServiceRequestMessage | ServiceAuthMessage
|
|
10
|
+
| ServiceProgressMessage | ServiceResponseMessage | ServiceErrorMessage
|
|
11
|
+
| ServiceAddEventListenerMessage | ServiceRemoveEventListenerMessage
|
|
12
|
+
| ServiceGetEventListenerInfosMessage
|
|
13
|
+
| ServiceEmitEventMessage | ServiceEventMessage;
|
|
14
|
+
|
|
15
|
+
type ServiceClientMessage = // 클라이언트 → 서버
|
|
16
|
+
| ServiceRequestMessage | ServiceAuthMessage
|
|
17
|
+
| ServiceAddEventListenerMessage | ServiceRemoveEventListenerMessage
|
|
18
|
+
| ServiceGetEventListenerInfosMessage | ServiceEmitEventMessage;
|
|
19
|
+
|
|
20
|
+
type ServiceServerMessage = // 서버 → 클라이언트 (디스패치 대상)
|
|
21
|
+
| ServiceResponseMessage | ServiceErrorMessage | ServiceEventMessage;
|
|
22
|
+
|
|
23
|
+
type ServiceServerRawMessage = // 서버 → 클라이언트 (progress 포함 전체)
|
|
24
|
+
| ServiceProgressMessage | ServiceServerMessage;
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 시스템
|
|
28
|
+
|
|
29
|
+
- `ServiceProgressMessage` — `name: "progress"`, `body: { totalSize, completedSize }`. 서버가 청크 수신 진행률 알림.
|
|
30
|
+
- `ServiceErrorMessage` — `name: "error"`, `body: { name, message, code, stack?, detail?, cause? }`. 처리 중 에러 알림.
|
|
31
|
+
- `ServiceAuthMessage` — `name: "auth"`, `body: string` (토큰). 클라이언트 인증.
|
|
32
|
+
|
|
33
|
+
## 서비스 메서드
|
|
34
|
+
|
|
35
|
+
- `ServiceRequestMessage` — `` name: `${string}.${string}` `` (예: `"User.findOne"`), `body: unknown[]` (매개변수 배열).
|
|
36
|
+
- `ServiceResponseMessage` — `name: "response"`, `body?: unknown` (반환값).
|
|
37
|
+
|
|
38
|
+
## 이벤트
|
|
39
|
+
|
|
40
|
+
- `ServiceAddEventListenerMessage` — `name: "evt:add"`, `body: { key, name, info }`. `key` 는 uuid (remove 키).
|
|
41
|
+
- `ServiceRemoveEventListenerMessage` — `name: "evt:remove"`, `body: { key }`.
|
|
42
|
+
- `ServiceGetEventListenerInfosMessage` — `name: "evt:gets"`, `body: { name }`. 동일 이벤트 구독자들의 `info` 목록 요청.
|
|
43
|
+
- `ServiceEmitEventMessage` — `name: "evt:emit"`, `body: { keys, data }`. 클라이언트 발생.
|
|
44
|
+
- `ServiceEventMessage` — `name: "evt:on"`, `body: { keys, data }`. 서버 알림.
|
|
45
|
+
|
|
46
|
+
## 사용
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
function dispatch(msg: ServiceServerMessage) {
|
|
50
|
+
switch (msg.name) {
|
|
51
|
+
case "response": return resolveCall(msg.body);
|
|
52
|
+
case "error": return rejectCall(msg.body);
|
|
53
|
+
case "evt:on": return notifyListeners(msg.body.keys, msg.body.data);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @simplysm/service-common — protocol
|
|
2
|
+
|
|
3
|
+
서비스 transport(웹소켓 등) 위에 얹는 바이너리 메시지 프로토콜(V2). 메시지를 청크로 자르고/재조립하며, 청크 누적의 GC 까지 캡슐화한다.
|
|
4
|
+
|
|
5
|
+
## PROTOCOL_CONFIG
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
const PROTOCOL_CONFIG = {
|
|
9
|
+
MAX_TOTAL_SIZE: 100 * 1024 * 1024, // 100MB 한계 (인/디코딩 양쪽에서 검사)
|
|
10
|
+
SPLIT_MESSAGE_SIZE: 3 * 1024 * 1024, // 이 크기 초과 시 청크 분할
|
|
11
|
+
CHUNK_SIZE: 300 * 1024, // 청크 본문 크기
|
|
12
|
+
GC_INTERVAL: 10 * 1000, // 청크 누적기 GC 주기
|
|
13
|
+
EXPIRE_TIME: 60 * 1000, // 미완성 메시지 만료
|
|
14
|
+
} as const;
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
`as const`. 임계값 조정 필요 시 이 상수를 참조한다.
|
|
18
|
+
|
|
19
|
+
## ServiceProtocol
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
interface ServiceProtocol {
|
|
23
|
+
encode(uuid: string, message: ServiceMessage): { chunks: Bytes[]; totalSize: number };
|
|
24
|
+
decode<T extends ServiceMessage>(bytes: Bytes): ServiceMessageDecodeResult<T>;
|
|
25
|
+
dispose(): void;
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- 청크 헤더 28B: UUID 16B + TotalSize 8B(uint64, 상위 4B 는 0) + Index 4B(uint32), Big Endian.
|
|
30
|
+
- 본문: `json.stringify([name, body?])` UTF-8.
|
|
31
|
+
- `dispose()` 는 내부 `LazyGcMap` 의 GC 타이머를 해제. 인스턴스 종료 시 반드시 호출.
|
|
32
|
+
|
|
33
|
+
## ServiceMessageDecodeResult
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
type ServiceMessageDecodeResult<T extends ServiceMessage> =
|
|
37
|
+
| { type: "complete"; uuid: string; message: T }
|
|
38
|
+
| { type: "progress"; uuid: string; totalSize: number; completedSize: number };
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `complete`: 모든 청크 수신·재조립 완료. 메시지 디스패치 가능.
|
|
42
|
+
- `progress`: 청크 일부만 도착. 진행률 표시·`progress` 알림 송신에 사용.
|
|
43
|
+
|
|
44
|
+
## createServiceProtocol()
|
|
45
|
+
|
|
46
|
+
`ServiceProtocol` 인스턴스를 생성한다. 서버·클라이언트 각자 1 개씩 보유하는 것이 일반적.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const proto = createServiceProtocol();
|
|
50
|
+
const { chunks } = proto.encode(uuid, { name: "User.findOne", body: [{ id: 1 }] });
|
|
51
|
+
for (const c of chunks) socket.send(c);
|
|
52
|
+
|
|
53
|
+
const res = proto.decode<ServiceServerMessage>(receivedBytes);
|
|
54
|
+
if (res.type === "complete") handle(res.message);
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
예외:
|
|
58
|
+
|
|
59
|
+
- `MAX_TOTAL_SIZE` 초과: `ArgumentError("메시지 크기가 제한을 초과했습니다.")`.
|
|
60
|
+
- 헤더(<28B) 미달: `ArgumentError("버퍼 크기가 헤더 크기보다 작습니다.")`.
|
|
61
|
+
- 본문 JSON 파싱 실패: `ArgumentError("메시지 디코딩에 실패했습니다.", { uuid, cause })`.
|
|
62
|
+
- 무결성 위반(누적 크기 > totalSize): `ArgumentError("프로토콜 무결성 위반: ...")`.
|
|
63
|
+
|
|
64
|
+
중복 청크는 인덱스가 이미 채워진 경우 무시(방어). 만료(`EXPIRE_TIME`) 시 누적 항목은 GC 가 폐기.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# @simplysm/service-common — service-types
|
|
2
|
+
|
|
3
|
+
서버 구현·클라이언트 호출이 공유하는 빌트인 서비스 인터페이스. 서버는 인터페이스를 구현, 클라이언트는 동일 시그니처로 RPC 호출한다.
|
|
4
|
+
|
|
5
|
+
## OrmService
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface OrmService {
|
|
9
|
+
getInfo(opt: DbConnOptions & { configName: string }): Promise<{ dialect: Dialect; database?: string; schema?: string }>;
|
|
10
|
+
connect(opt: DbConnOptions & { configName: string }): Promise<number>; // connId
|
|
11
|
+
close(connId: number): Promise<void>;
|
|
12
|
+
beginTransaction(connId: number, isolationLevel?: IsolationLevel): Promise<void>;
|
|
13
|
+
commitTransaction(connId: number): Promise<void>;
|
|
14
|
+
rollbackTransaction(connId: number): Promise<void>;
|
|
15
|
+
executeParametrized(connId: number, query: string, params?: unknown[]): Promise<unknown[][]>;
|
|
16
|
+
executeDefs(connId: number, defs: QueryDef[], options?: (ResultMeta | undefined)[]): Promise<unknown[][]>;
|
|
17
|
+
bulkInsert(connId: number, tableName: string, columnDefs: Record<string, ColumnMeta>, records: Record<string, unknown>[]): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type DbConnOptions = { configName?: string; config?: Record<string, unknown> };
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
MySQL / MSSQL / PostgreSQL 공통 추상. `connId` 단위로 트랜잭션·세션 식별. `Dialect`·`IsolationLevel`·`QueryDef`·`ColumnMeta`·`ResultMeta` 는 `@simplysm/orm-common` 정의.
|
|
24
|
+
|
|
25
|
+
## AutoUpdateService
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
interface AutoUpdateService {
|
|
29
|
+
getLastVersion(platform: string): Promise<{ version: string; downloadPath: string } | undefined>;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`platform` 예: `"win32" | "darwin" | "linux"`. 등록된 버전 없으면 `undefined`.
|
|
34
|
+
|
|
35
|
+
## AppStructureService
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
interface AppStructureService {
|
|
39
|
+
getItems(): Record<string, AppStructureItem[]>;
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
키는 클라이언트명, 값은 해당 클라이언트의 앱 구조 트리. `AppStructureItem` 은 [app-structure.md](./app-structure.md) 참조.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# @simplysm/service-server
|
|
2
|
+
|
|
3
|
+
Fastify 기반 서비스 서버. HTTP/WebSocket 양 전송 위에서 "서비스(`이름`+`메서드 맵`)" 단위로 RPC, JWT 인증, 정적 파일, 업로드, 이벤트 브로드캐스트, V1 레거시 호환을 제공한다.
|
|
4
|
+
|
|
5
|
+
## 사용 트리거 인덱스
|
|
6
|
+
|
|
7
|
+
- **`ServiceServer` / `createServiceServer` / `ServiceServerOptions`** — 서버 인스턴스 생성·기동·종료·이벤트 브로드캐스트·JWT 발급. 자세히: [server.md](./server.md)
|
|
8
|
+
- **`defineService` / `auth` / `ServiceContext` / `ServiceMethods`** — 서비스 정의 및 메서드/팩토리 인증 래퍼, 컨텍스트 헬퍼. 자세히: [define-service.md](./define-service.md)
|
|
9
|
+
- **`AuthTokenPayload` / `signJwt` / `verifyJwt` / `decodeJwt`** — JWT 토큰 페이로드 타입 및 직접 서명/검증 유틸. 자세히: [auth.md](./auth.md)
|
|
10
|
+
- **빌트인 서비스 (`OrmService` / `AutoUpdateService` / `AppStructureService`)** — `services` 옵션에 그대로 등록해 ORM 프록시·자동 업데이트·앱 메뉴 트리 제공. 자세히: [builtin-services.md](./builtin-services.md)
|
|
11
|
+
- **전송/프로토콜/레거시 내부** (`handleHttpRequest`, `handleUpload`, `handleStaticFile`, `createWebSocketHandler`, `createServiceSocket`, `createServerProtocolWrapper`, `handleV1Connection` 등) — `ServiceServer.listen()` 이 자동 사용. 커스텀 Fastify 라우트 직조 시에만 직접 호출. 자세히: [internals.md](./internals.md)
|
|
12
|
+
|
|
13
|
+
## `getConfig`
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { getConfig } from "@simplysm/service-server";
|
|
17
|
+
const conf = await getConfig<{ orm: { default: DbConnConfig } }>(filePath);
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`<rootPath>/.config.json` 같은 JSON 설정 파일을 읽고 LazyGcMap 캐시 + `FsWatcher` 로 핫리로드한다. 보통은 `ctx.getConfig(section)` 으로 충분하며, 이 함수를 직접 호출할 일은 root/client 경로 외 설정 파일을 읽을 때만 있다.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @simplysm/service-server — auth
|
|
2
|
+
|
|
3
|
+
JWT 페이로드 타입 및 서명/검증 유틸. 일반 시나리오에서는 `server.signAuthToken` / `server.verifyAuthToken` 이 동일 알고리즘으로 감싸므로 그쪽을 쓴다. 이 모듈을 직접 import 하는 경우는 토큰 발급/검증을 서버 인스턴스 없이 수행해야 할 때 (테스트, 별도 인증 서비스, 디코드 등).
|
|
4
|
+
|
|
5
|
+
## `AuthTokenPayload<TAuthInfo>`
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface AuthTokenPayload<TAuthInfo = unknown> extends jose.JWTPayload {
|
|
9
|
+
roles: string[]; // auth(["role"], fn) 검사 대상
|
|
10
|
+
data: TAuthInfo; // ctx.authInfo 로 노출
|
|
11
|
+
}
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## `signJwt(jwtSecret, payload) → Promise<string>`
|
|
15
|
+
|
|
16
|
+
HS256, `iat` 자동, 만료 `12h` 고정. 토큰 수명을 바꿔야 하면 이 함수 대신 직접 `jose.SignJWT` 를 쓴다.
|
|
17
|
+
|
|
18
|
+
## `verifyJwt<TAuthInfo>(jwtSecret, token) → Promise<AuthTokenPayload<TAuthInfo>>`
|
|
19
|
+
|
|
20
|
+
검증 실패 시 `토큰이 만료되었습니다.` 또는 `유효하지 않은 토큰입니다.` 를 throw.
|
|
21
|
+
|
|
22
|
+
## `decodeJwt<TAuthInfo>(token) → AuthTokenPayload<TAuthInfo>`
|
|
23
|
+
|
|
24
|
+
서명 검증 없이 페이로드만 디코드. 클라이언트 측 만료 표시 등 비보안 용도에만.
|
|
25
|
+
|
|
26
|
+
## 예제
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
const token = await signJwt(secret, { roles: ["admin"], data: { userId: "U1" } });
|
|
30
|
+
const payload = await verifyJwt<{ userId: string }>(secret, token);
|
|
31
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @simplysm/service-server — builtin-services
|
|
2
|
+
|
|
3
|
+
`ServiceServerOptions.services` 에 그대로 푸시해서 쓰는 사전 정의 서비스. 각각의 `*ServiceType` 은 클라이언트 측에서 `client.getService<*ServiceType>("<이름>")` 로 타입 공유한다.
|
|
4
|
+
|
|
5
|
+
## `OrmService` / `OrmServiceType`
|
|
6
|
+
|
|
7
|
+
이름 alias: `["Orm", "SdOrmService"]`. **WebSocket 전용** (HTTP 호출 시 throw — 트랜잭션을 소켓 라이프타임에 묶기 위함). `auth()` 로 감싸져 있어 로그인 필요.
|
|
8
|
+
|
|
9
|
+
설정: `ctx.getConfig("orm")` 으로 `Record<string, DbConnConfig>` 를 읽고 `opt.configName` 으로 선택. 클라이언트가 `opt.config` 로 일부 필드 override 가능.
|
|
10
|
+
|
|
11
|
+
메서드: `getInfo`, `connect → connId`, `close(connId)`, `beginTransaction(connId, isolation?)`, `commitTransaction`, `rollbackTransaction`, `executeParametrized(connId, sql, params?)`, `executeDefs(connId, defs, optionsMeta?)`, `bulkInsert(connId, table, colDefs, records)`. 소켓 close 시 그 소켓의 모든 연결을 자동 정리.
|
|
12
|
+
|
|
13
|
+
## `AutoUpdateService` / `AutoUpdateServiceType`
|
|
14
|
+
|
|
15
|
+
이름 alias: `["AutoUpdate", "SdAutoUpdateService"]`. 인증 없음.
|
|
16
|
+
|
|
17
|
+
`getLastVersion(platform)` — `<clientPath>/<platform>/updates/` 디렉토리에서 `android` 는 `.apk`, 그 외는 `.exe` 중 `semver.maxSatisfying("*")` 으로 최신을 골라 `{ version, downloadPath }` 반환. 없으면 `undefined`.
|
|
18
|
+
|
|
19
|
+
## `AppStructureService(itemsMap) → ServiceDefinition` / `AppStructureServiceType`
|
|
20
|
+
|
|
21
|
+
다른 두 서비스와 달리 **팩토리 호출 결과**를 등록한다. 이름 `"AppStructure"`, 인증 없음.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { AppStructureService } from "@simplysm/service-server";
|
|
25
|
+
import type { AppStructureItem } from "@simplysm/service-common";
|
|
26
|
+
|
|
27
|
+
const itemsMap: Record<string, AppStructureItem[]> = { admin: [...], shop: [...] };
|
|
28
|
+
services: [AppStructureService(itemsMap)];
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`getItems()` 한 메서드만 노출. 클라이언트 메뉴/라우트 트리 공급용.
|
|
32
|
+
|
|
33
|
+
## 등록 예제
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { createServiceServer, OrmService, AutoUpdateService, AppStructureService } from "@simplysm/service-server";
|
|
37
|
+
|
|
38
|
+
createServiceServer({
|
|
39
|
+
rootPath, port: 50080, auth: { jwtSecret },
|
|
40
|
+
services: [
|
|
41
|
+
OrmService,
|
|
42
|
+
AutoUpdateService,
|
|
43
|
+
AppStructureService(appItemsMap),
|
|
44
|
+
MyAppService,
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
```
|