@maestro-js/agent-skills 1.0.0-alpha.2 → 1.0.0-alpha.21
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/bin.js +19 -0
- package/package.json +8 -7
- package/{dist → skills}/choosing-an-npm-package/SKILL.md +5 -4
- package/skills/iconify/SKILL.md +82 -0
- package/skills/iconify/scripts/iconify.js +111 -0
- package/skills/iso-fns/SKILL.md +137 -0
- package/skills/iso-fns/references/api_reference.md +435 -0
- package/skills/maestro-architecture/SKILL.md +552 -0
- package/skills/maestro-examples/SKILL.md +140 -0
- package/skills/maestro-examples/references/cadence/cadence-web/.auth/user.json +15 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/app.css +112 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/auth-layout.tsx +39 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/back-link.tsx +24 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/button-group.tsx +78 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/button-link.tsx +77 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/button.tsx +139 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/checkbox.tsx +210 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/chip.tsx +161 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/container.tsx +85 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/date-range.tsx +141 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/date-time-picker.tsx +199 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/empty-state.tsx +19 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/form-page.tsx +41 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/form.tsx +106 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/helpers/animated-popover.tsx +27 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/helpers/button-context.ts +13 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/helpers/calendar-month-year-picker.tsx +283 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/helpers/form-field.tsx +231 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/helpers/get-button-classes.ts +141 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/helpers/headless-button.tsx +39 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/icon.tsx +29 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/inline-alert.tsx +57 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/interaction-form.tsx +87 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/menu.tsx +189 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/optional-link.tsx +18 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/page-header.tsx +19 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/rich-text-editor.tsx +352 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/select.tsx +219 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/spinner.tsx +19 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/headless-templated-row-table.ts +553 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/index.tsx +44 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/server-table.ts +146 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-actions.tsx +79 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-container.tsx +176 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-container.type-test.ts +158 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-filters/date-range-filter.tsx +230 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-filters/select-filter.tsx +214 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-filters/sort.tsx +88 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-primitives.tsx +229 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-toolbar.tsx +303 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/table-types.ts +64 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/use-client-table.ts +160 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/use-client-table.type-test.ts +220 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/use-server-table.ts +195 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/table/use-server-table.type-test.ts +177 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/tag-editor.tsx +53 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/tag-field.tsx +253 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/text-field.tsx +147 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/toast.tsx +182 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/components/top-nav.tsx +175 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/entry.client.tsx +21 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/entry.server.tsx +13 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/features/csrf-middleware.server.ts +17 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/features/custom-error-status-code-middleware.server.ts +31 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/features/rate-limiting-middleware.server.ts +36 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/features/redirect-with-toast.server.ts +14 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/features/request-context.server.ts +40 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/features/security-headers-middleware.server.ts +10 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/icons/types.ts +36 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/root.tsx +208 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/_index.tsx +5 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/activity.tsx +124 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/archive.tsx +187 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/auth.forgot-password.tsx +108 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/auth.login.tsx +132 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/auth.logout.tsx +17 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/auth.register.tsx +172 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/auth.reset-password.tsx +137 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/contacts.$id.edit.tsx +89 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/contacts.$id.tsx +521 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/contacts.new.tsx +56 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/contacts.tsx +183 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/settings.tsx +158 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes/users.tsx +263 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/routes.ts +4 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/services.server.ts +1 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/colors.ts +47 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/compose-event-handlers.ts +13 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/compose-refs.ts +35 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/format-date.ts +10 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/overridable-component-helper.ts +31 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/sanitize-html.ts +16 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/tw.ts +16 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/use-hydrated.ts +12 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/use-prevent-default.ts +30 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/use-query-state.ts +629 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/use-render-props.ts +42 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/use-spin-delay.ts +27 -0
- package/skills/maestro-examples/references/cadence/cadence-web/app/utils/use-stable-accessor.ts +14 -0
- package/skills/maestro-examples/references/cadence/cadence-web/instrument.server.mjs +29 -0
- package/skills/maestro-examples/references/cadence/cadence-web/package.json +60 -0
- package/skills/maestro-examples/references/cadence/cadence-web/playwright.config.ts +39 -0
- package/skills/maestro-examples/references/cadence/cadence-web/react-router.config.ts +12 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tailwind.config.ts +7 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/activity/filter-activity.spec.ts +93 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/activity/recent-activity.spec.ts +70 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/archive/archive-view.spec.ts +62 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/auth/login-invalid.spec.ts +68 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/auth/login-valid.spec.ts +45 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/auth/logout.spec.ts +46 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/auth/password-reset.spec.ts +105 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/auth/register.spec.ts +39 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/auth.setup.ts +48 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/authorization/member-ownership.spec.ts +104 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/authorization/viewer-read-only.spec.ts +103 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/constants.ts +11 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/archive-restore.spec.ts +77 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/create-contact.spec.ts +56 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/delete-contact.spec.ts +53 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/edit-contact.spec.ts +60 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/filter-by-last-contacted.spec.ts +85 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/filter-by-tag.spec.ts +68 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/global-search.spec.ts +57 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/sort-contacts.spec.ts +60 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/tags.spec.ts +85 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/contacts/view-detail.spec.ts +58 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/interactions/delete-interaction.spec.ts +59 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/interactions/edit-interaction.spec.ts +68 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/interactions/log-email.spec.ts +57 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/interactions/log-note.spec.ts +56 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/interactions/rich-text-formatting.spec.ts +97 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/organization/delete.spec.ts +70 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/organization/edit-name.spec.ts +51 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/users/change-role.spec.ts +62 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/users/invite-user.spec.ts +56 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tests/users/remove-user.spec.ts +58 -0
- package/skills/maestro-examples/references/cadence/cadence-web/tsconfig.json +22 -0
- package/skills/maestro-examples/references/cadence/cadence-web/vite.config.ts +37 -0
- package/skills/maestro-examples/references/cadence/config/agent-container.ts +4 -0
- package/skills/maestro-examples/references/cadence/config/cadence-web.ts +12 -0
- package/skills/maestro-examples/references/cadence/config/db-migrate.ts +9 -0
- package/skills/maestro-examples/references/cadence/config/queue-processor.ts +11 -0
- package/skills/maestro-examples/references/cadence/config/services.ts +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/20260404000001_organizations-and-users.ts +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/20260404000002_contacts.ts +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/20260404000003_interactions.ts +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/20260404000004_remember-me-tokens.ts +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/20260404000005_password-reset-tokens.ts +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/20260404000006_queue-and-recurring-jobs.ts +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000001_organizations-and-users-down.sql +2 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000001_organizations-and-users-up.sql +24 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000002_contacts-down.sql +1 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000002_contacts-up.sql +16 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000003_interactions-down.sql +1 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000003_interactions-up.sql +15 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000004_remember-me-tokens-down.sql +1 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000004_remember-me-tokens-up.sql +12 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000005_password-reset-tokens-down.sql +1 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000005_password-reset-tokens-up.sql +11 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000006_queue-and-recurring-jobs-down.sql +2 -0
- package/skills/maestro-examples/references/cadence/infrastructure/db-migrations/sqls/20260404000006_queue-and-recurring-jobs-up.sql +21 -0
- package/skills/maestro-examples/references/cadence/package.json +17 -0
- package/skills/maestro-examples/references/cadence/packages/schemas/package.json +17 -0
- package/skills/maestro-examples/references/cadence/packages/schemas/src/contact/index.ts +57 -0
- package/skills/maestro-examples/references/cadence/packages/schemas/src/index.ts +4 -0
- package/skills/maestro-examples/references/cadence/packages/schemas/src/interaction/index.ts +45 -0
- package/skills/maestro-examples/references/cadence/packages/schemas/src/organization/index.ts +32 -0
- package/skills/maestro-examples/references/cadence/packages/schemas/src/user/index.ts +69 -0
- package/skills/maestro-examples/references/cadence/packages/schemas/tsconfig.json +9 -0
- package/skills/maestro-examples/references/cadence/packages/services/package.json +43 -0
- package/skills/maestro-examples/references/cadence/packages/services/run-tests.ts +59 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/auth/auth-db-provider.ts +202 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/auth/auth-use-cases.ts +155 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/auth/index.ts +17 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/auth/profile-db-provider.ts +17 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/contact/contact-db-provider.test.ts +120 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/contact/contact-db-provider.ts +145 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/contact/contact-use-cases.ts +196 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/contact/index.ts +29 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/index.ts +23 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/interaction/index.ts +25 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/interaction/interaction-db-provider.test.ts +128 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/interaction/interaction-db-provider.ts +98 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/interaction/interaction-use-cases.ts +171 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/maestro.ts +218 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/organization/index.ts +8 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/organization/organization-db-provider.test.ts +53 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/organization/organization-db-provider.ts +60 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/organization/organization-use-cases.ts +88 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/permissions.ts +28 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/testing/contact-seed.ts +30 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/testing/index.ts +13 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/testing/interaction-seed.ts +29 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/testing/organization-seed.ts +25 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/testing/seed-pipe.ts +38 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/testing/user-seed.ts +27 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/user/index.ts +27 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/user/user-db-provider.test.ts +100 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/user/user-db-provider.ts +69 -0
- package/skills/maestro-examples/references/cadence/packages/services/src/user/user-use-cases.ts +162 -0
- package/skills/maestro-examples/references/cadence/packages/services/tsconfig.json +9 -0
- package/skills/maestro-examples/references/cadence/scripts/foreign-key-delete-behavior.ts +106 -0
- package/skills/maestro-examples/references/cadence/specs/app.md +72 -0
- package/skills/maestro-examples/references/cadence/specs/data-models.md +80 -0
- package/skills/maestro-examples/references/cadence/specs/multi-tenant-security.md +239 -0
- package/skills/maestro-examples/references/cadence/specs/rich-text-editor.md +108 -0
- package/skills/maestro-examples/references/cadence/specs/table.md +151 -0
- package/skills/maestro-examples/references/cadence/specs/use-cases.md +62 -0
- package/skills/maestro-examples/references/cadence/tsconfig.json +29 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/app.css +152 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/alert-dialog.tsx +153 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/avatar.tsx +57 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/button-group.tsx +78 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/button-link.tsx +77 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/button.tsx +139 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/channel-card.tsx +40 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/checkbox-group.tsx +175 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/checkbox.tsx +210 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/chip.tsx +161 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/container.tsx +85 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/date-picker.tsx +214 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/date-time-picker.tsx +199 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/dialog.tsx +114 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/enum-chip.tsx +33 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/file-input.tsx +248 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/form.tsx +106 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/headless-file-input.tsx +365 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/helpers/animated-popover.tsx +27 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/helpers/button-context.ts +13 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/helpers/calendar-month-year-picker.tsx +283 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/helpers/form-field.tsx +231 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/helpers/get-button-classes.ts +141 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/helpers/headless-button.tsx +39 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/helpers/pdf-dist.client.ts +9 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/icon.tsx +29 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/image-grid.tsx +96 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/image-input.tsx +268 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/inline-alert.tsx +57 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/like-button.tsx +48 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/menu.tsx +189 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/pdf-input.tsx +252 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/pdf.tsx +89 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/post-card.tsx +147 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/post-composer.tsx +166 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/radio-group.tsx +115 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/select.tsx +219 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/spinner.tsx +19 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/tag-field.tsx +253 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/text-area.tsx +151 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/text-field.tsx +147 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/components/toast.tsx +179 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/entry.client.tsx +21 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/entry.server.tsx +12 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/features/csrf-hidden-input.tsx +6 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/features/csrf-middleware.server.ts +17 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/features/custom-error-status-code-middleware.server.ts +18 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/features/rate-limiting-middleware.server.ts +36 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/features/redirect-with-toast.server.ts +14 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/features/request-context.server.ts +40 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/features/security-headers-middleware.server.ts +10 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/icons/types.ts +26 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/root.tsx +164 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_auth/forgot-password.tsx +90 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_auth/login.tsx +104 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_auth/logout.tsx +44 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_auth/reset-password.tsx +130 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_auth/signup.tsx +91 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_browse/_layout.tsx +36 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_browse/c.new.tsx +47 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/_browse/index.tsx +3 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/api/hide-post.$postId.tsx +30 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/api/like.$postId.tsx +22 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/c.$slug/_layout.tsx +108 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/c.$slug/post.new.tsx +59 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/c.$slug.edit.tsx +53 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/c.$slug.manage/_layout.tsx +123 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/c.$slug.manage/add-moderator.tsx +48 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/c.$slug.manage/transfer-ownership.tsx +71 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/c.$slug.post.$postId.tsx +119 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/files.$.tsx +22 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/health.tsx +3 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes/settings.tsx +128 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/routes.ts +4 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/services.server.ts +1 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/colors.ts +47 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/compose-refs.ts +35 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/enum-colors.ts +4 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/file-input.ts +52 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/tw.ts +16 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-element-size.ts +35 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-number-input.ts +143 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-pagination.ts +41 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-prevent-default.ts +30 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-profile.ts +7 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-render-props.ts +42 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-spin-delay.ts +27 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-stable-accessor.ts +14 -0
- package/skills/maestro-examples/references/chorus/chorus-web/app/utils/use-tab-indicator.ts +107 -0
- package/skills/maestro-examples/references/chorus/chorus-web/environment.d.ts +15 -0
- package/skills/maestro-examples/references/chorus/chorus-web/instrument.server.mjs +29 -0
- package/skills/maestro-examples/references/chorus/chorus-web/package.json +54 -0
- package/skills/maestro-examples/references/chorus/chorus-web/playwright.config.ts +39 -0
- package/skills/maestro-examples/references/chorus/chorus-web/react-router.config.ts +12 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/auth-landing.test.ts +8 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/auth-login.test.ts +40 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/auth-logout.test.ts +8 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/auth-signup.test.ts +15 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/auth.setup.ts +42 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/channel-browse.test.ts +43 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/channel-create.test.ts +34 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/channel-edit.test.ts +33 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/channel-manage-moderators.test.ts +59 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/channel-manage-permissions.test.ts +55 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/channel-transfer.test.ts +47 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/constants.ts +7 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/forgot-password.test.ts +33 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/hide-post-author.test.ts +62 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/hide-post-nonauthor.test.ts +36 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/like-toggle.test.ts +60 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/like-unauth.test.ts +37 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/post-create.test.ts +30 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/post-listing.test.ts +33 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/post-reply.test.ts +48 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/post-view-unauth.test.ts +37 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/settings-duplicate.test.ts +28 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/settings-update.test.ts +34 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/settings-view.test.ts +17 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tests/signup-validation.test.ts +51 -0
- package/skills/maestro-examples/references/chorus/chorus-web/tsconfig.json +14 -0
- package/skills/maestro-examples/references/chorus/chorus-web/vite.config.ts +38 -0
- package/skills/maestro-examples/references/chorus/config/app-a.ts +7 -0
- package/skills/maestro-examples/references/chorus/config/chorus-web.ts +7 -0
- package/skills/maestro-examples/references/chorus/config/db-migrate.ts +9 -0
- package/skills/maestro-examples/references/chorus/config/queue-processor.ts +7 -0
- package/skills/maestro-examples/references/chorus/config/services.ts +15 -0
- package/skills/maestro-examples/references/chorus/infrastructure/cdk/index.ts +60 -0
- package/skills/maestro-examples/references/chorus/infrastructure/cdk.context.json +5 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/20260303005902_init_authentication.ts +16 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/20260308234835_add-maestro-queue-table.ts +16 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/20260315051819_channels.ts +16 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/20260315052202_channel-moderators.ts +16 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/20260315052539_posts.ts +16 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/20260315052734_post-images.ts +16 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/20260315053110_likes.ts +16 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260303005902_init_authentication-down.sql +3 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260303005902_init_authentication-up.sql +40 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260308234835_add-maestro-queue-table-down.sql +3 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260308234835_add-maestro-queue-table-up.sql +23 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315051819_channels-down.sql +1 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315051819_channels-up.sql +11 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315052202_channel-moderators-down.sql +1 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315052202_channel-moderators-up.sql +7 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315052539_posts-down.sql +1 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315052539_posts-up.sql +14 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315052734_post-images-down.sql +1 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315052734_post-images-up.sql +7 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315053110_likes-down.sql +1 -0
- package/skills/maestro-examples/references/chorus/infrastructure/db-migrations/sqls/20260315053110_likes-up.sql +8 -0
- package/skills/maestro-examples/references/chorus/infrastructure/package.json +20 -0
- package/skills/maestro-examples/references/chorus/infrastructure/tsconfig.json +8 -0
- package/skills/maestro-examples/references/chorus/package.json +17 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/other/generate-email-modules.ts +22 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/other/generate-email-previews.ts +18 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/package.json +27 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/components/button.tsx +19 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/components/root-container.tsx +29 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/components/tailwind-wrapper.tsx +113 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/email-modules.ts +9 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/emails/password-reset.tsx +41 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/index.ts +3 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/render-email-template.ts +24 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/src/utils/tw.ts +5 -0
- package/skills/maestro-examples/references/chorus/packages/email-templates/tsconfig.json +11 -0
- package/skills/maestro-examples/references/chorus/packages/schemas/package.json +17 -0
- package/skills/maestro-examples/references/chorus/packages/schemas/src/channel/index.ts +41 -0
- package/skills/maestro-examples/references/chorus/packages/schemas/src/index.ts +4 -0
- package/skills/maestro-examples/references/chorus/packages/schemas/src/like/index.ts +16 -0
- package/skills/maestro-examples/references/chorus/packages/schemas/src/post/index.ts +44 -0
- package/skills/maestro-examples/references/chorus/packages/schemas/src/profile/index.ts +80 -0
- package/skills/maestro-examples/references/chorus/packages/schemas/tsconfig.json +9 -0
- package/skills/maestro-examples/references/chorus/packages/services/environment.d.ts +7 -0
- package/skills/maestro-examples/references/chorus/packages/services/package.json +47 -0
- package/skills/maestro-examples/references/chorus/packages/services/run-tests.ts +59 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/auth/auth-use-cases.ts +178 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/auth/authenticatable-db-provider.ts +59 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/auth/index.ts +29 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/auth/password-reset-token-db-provider.ts +58 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/auth/remember-me-token-db-provider.ts +82 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/channel/channel-db-provider.test.ts +85 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/channel/channel-db-provider.ts +158 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/channel/channel-use-cases.ts +303 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/channel/index.ts +29 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/index.ts +21 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/like/index.ts +6 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/like/like-db-provider.test.ts +47 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/like/like-db-provider.ts +53 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/like/like-use-cases.ts +31 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/maestro.ts +215 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/permissions.ts +16 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/post/index.ts +25 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/post/post-db-provider.test.ts +68 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/post/post-db-provider.ts +219 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/post/post-use-cases.ts +184 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/post/redact-hidden-post.ts +4 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/profile/index.ts +17 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/profile/profile-db-provider.test.ts +57 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/profile/profile-db-provider.ts +75 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/profile/profile-use-cases.ts +61 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/testing/channel-seed.ts +27 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/testing/index.ts +13 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/testing/like-seed.ts +21 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/testing/post-seed.ts +27 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/testing/profile-seed.ts +28 -0
- package/skills/maestro-examples/references/chorus/packages/services/src/testing/seed-pipe.ts +38 -0
- package/skills/maestro-examples/references/chorus/packages/services/tsconfig.json +9 -0
- package/skills/maestro-examples/references/chorus/scripts/foreign-key-delete-behavior.ts +111 -0
- package/skills/maestro-examples/references/chorus/specs/app.md +73 -0
- package/skills/maestro-examples/references/chorus/specs/data-models.md +48 -0
- package/skills/maestro-examples/references/chorus/specs/database-schema.md +59 -0
- package/skills/maestro-examples/references/chorus/specs/use-cases.md +33 -0
- package/skills/maestro-examples/references/chorus/tsconfig.json +29 -0
- package/skills/maestro-examples/references/setlist/IMPLEMENTATION_PLAN.md +88 -0
- package/skills/maestro-examples/references/setlist/config/db-migrate.ts +9 -0
- package/skills/maestro-examples/references/setlist/config/queue-processor.ts +11 -0
- package/skills/maestro-examples/references/setlist/config/services.ts +16 -0
- package/skills/maestro-examples/references/setlist/config/setlist-web.ts +12 -0
- package/skills/maestro-examples/references/setlist/infrastructure/cdk/index.ts +56 -0
- package/skills/maestro-examples/references/setlist/infrastructure/cdk.context.json +5 -0
- package/skills/maestro-examples/references/setlist/infrastructure/db-migrations/20260316000000_init.ts +16 -0
- package/skills/maestro-examples/references/setlist/infrastructure/db-migrations/20260317000000_add-queue-and-recurring-job-tables.ts +22 -0
- package/skills/maestro-examples/references/setlist/infrastructure/db-migrations/sqls/20260316000000_init-down.sql +5 -0
- package/skills/maestro-examples/references/setlist/infrastructure/db-migrations/sqls/20260316000000_init-up.sql +68 -0
- package/skills/maestro-examples/references/setlist/infrastructure/db-migrations/sqls/20260317000000_add-queue-and-recurring-job-tables-down.sql +2 -0
- package/skills/maestro-examples/references/setlist/infrastructure/db-migrations/sqls/20260317000000_add-queue-and-recurring-job-tables-up.sql +21 -0
- package/skills/maestro-examples/references/setlist/infrastructure/package.json +20 -0
- package/skills/maestro-examples/references/setlist/infrastructure/tsconfig.json +8 -0
- package/skills/maestro-examples/references/setlist/package.json +19 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/other/generate-email-modules.ts +22 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/other/generate-email-previews.ts +18 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/package.json +27 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/components/button.tsx +19 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/components/root-container.tsx +29 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/components/tailwind-wrapper.tsx +113 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/email-modules.ts +9 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/emails/magic-link-login.tsx +49 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/index.ts +3 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/render-email-template.ts +24 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/src/utils/tw.ts +5 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/tsconfig.json +11 -0
- package/skills/maestro-examples/references/setlist/packages/email-templates/tsup.config.ts +10 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/package.json +21 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/src/area/index.ts +28 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/src/index.ts +6 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/src/magic-link/index.ts +20 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/src/position.ts +137 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/src/project/index.ts +44 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/src/task/index.ts +41 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/src/user/index.ts +28 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/tests/position.test.ts +106 -0
- package/skills/maestro-examples/references/setlist/packages/schemas/tsconfig.json +9 -0
- package/skills/maestro-examples/references/setlist/packages/services/package.json +40 -0
- package/skills/maestro-examples/references/setlist/packages/services/run-tests.ts +59 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/area/area-db-provider.test.ts +29 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/area/area-db-provider.ts +81 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/area/area-use-cases.ts +130 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/area/index.ts +27 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/auth-db-provider.test.ts +34 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/auth-db-provider.ts +24 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/auth-use-cases.ts +110 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/index.ts +18 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/magic-link-db-provider.test.ts +52 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/magic-link-db-provider.ts +59 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/user-db-provider.test.ts +33 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/user-db-provider.ts +55 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/auth/verify-magic-link-credentials.ts +39 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/index.ts +20 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/maestro.ts +175 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/project/index.ts +29 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/project/project-db-provider.test.ts +46 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/project/project-db-provider.ts +114 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/project/project-use-cases.ts +183 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/task/index.ts +39 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/task/task-db-provider.test.ts +53 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/task/task-db-provider.ts +219 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/task/task-use-cases.ts +303 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/testing/area-seed.ts +25 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/testing/index.ts +13 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/testing/project-seed.ts +29 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/testing/seed-pipe.ts +38 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/testing/task-seed.ts +31 -0
- package/skills/maestro-examples/references/setlist/packages/services/src/testing/user-seed.ts +24 -0
- package/skills/maestro-examples/references/setlist/packages/services/tsconfig.json +9 -0
- package/skills/maestro-examples/references/setlist/queue-processor/package.json +17 -0
- package/skills/maestro-examples/references/setlist/queue-processor/src/index.ts +29 -0
- package/skills/maestro-examples/references/setlist/queue-processor/src/recurring-jobs.ts +13 -0
- package/skills/maestro-examples/references/setlist/queue-processor/tsconfig.json +4 -0
- package/skills/maestro-examples/references/setlist/scripts/foreign-key-delete-behavior.ts +107 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/app.css +158 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/alert-dialog.tsx +153 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/app-dnd-provider.tsx +180 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/area-form.tsx +113 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/button-group.tsx +78 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/button.tsx +139 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/checkbox.tsx +210 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/context-menu.tsx +78 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/date-picker.tsx +214 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/dnd-collision-detection.ts +28 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/dnd-utils.ts +11 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/drag-ghost.tsx +24 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/form.tsx +107 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/helpers/animated-popover.tsx +27 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/helpers/button-context.ts +13 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/helpers/calendar-month-year-picker.tsx +283 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/helpers/form-field.tsx +231 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/helpers/get-button-classes.ts +141 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/helpers/headless-button.tsx +39 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/icon.tsx +29 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/inline-alert.tsx +57 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/project-form.tsx +153 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/select.tsx +219 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/sidebar/helpers.ts +73 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/sidebar/index.ts +6 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/sidebar/inline-inputs.tsx +128 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/sidebar/sidebar.tsx +333 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/sidebar/sortable-rows.tsx +256 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/sidebar/use-sidebar-dnd.ts +261 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/spinner.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-inline-create.tsx +51 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/index.ts +3 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/task-date-helpers.ts +16 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/task-expanded-detail.tsx +273 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/task-keyboard-helpers.ts +5 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/task-list.tsx +134 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/task-row.tsx +100 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/types.ts +49 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/use-expanded-cache.ts +22 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/use-sticky-task.ts +24 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/task-list/use-task-reorder.ts +140 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/text-area.tsx +151 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/text-field.tsx +147 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/components/toast.tsx +179 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/entry.client.tsx +21 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/entry.server.tsx +13 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/features/csrf-middleware.server.ts +17 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/features/custom-error-status-code-middleware.server.ts +31 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/features/rate-limiting-middleware.server.ts +36 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/features/redirect-with-toast.server.ts +14 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/features/request-context.server.ts +52 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/features/security-headers-middleware.server.ts +10 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/hooks/use-area-actions.ts +44 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/hooks/use-csrf-token.ts +6 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/hooks/use-project-actions.ts +80 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/hooks/use-task-actions.ts +65 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/root.tsx +218 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/areas.$id.delete.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/areas.$id.edit.tsx +27 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/areas.$id.reorder.tsx +30 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/areas.tsx +23 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/projects.$id.complete.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/projects.$id.delete.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/projects.$id.edit.tsx +32 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/projects.$id.move.tsx +23 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/projects.$id.reorder.tsx +30 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/projects.$id.uncomplete.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/projects.tsx +23 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/tasks.$id.complete.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/tasks.$id.delete.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/tasks.$id.edit.tsx +27 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/tasks.$id.move.tsx +30 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/tasks.$id.reorder.tsx +32 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/tasks.$id.uncomplete.tsx +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/api/tasks.tsx +23 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/areas.$id.tsx +155 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/auth.login.tsx +98 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/auth.logout.tsx +17 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/auth.verify.tsx +154 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/completed.tsx +121 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/inbox.tsx +68 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/projects.$id.tsx +217 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/today.tsx +86 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes/upcoming.tsx +83 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/routes.ts +4 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/services.server.ts +1 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/colors.ts +47 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/compose-refs.ts +35 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/tw.ts +16 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/use-dismiss.ts +27 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/use-fetcher-complete.ts +17 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/use-prevent-default.ts +30 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/use-render-props.ts +42 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/use-spin-delay.ts +27 -0
- package/skills/maestro-examples/references/setlist/setlist-web/app/utils/use-stable-accessor.ts +14 -0
- package/skills/maestro-examples/references/setlist/setlist-web/environment.d.ts +8 -0
- package/skills/maestro-examples/references/setlist/setlist-web/instrument.server.mjs +29 -0
- package/skills/maestro-examples/references/setlist/setlist-web/knip.json +3 -0
- package/skills/maestro-examples/references/setlist/setlist-web/package.json +52 -0
- package/skills/maestro-examples/references/setlist/setlist-web/playwright.config.ts +39 -0
- package/skills/maestro-examples/references/setlist/setlist-web/react-router.config.ts +12 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-create.test.ts +37 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-delete.test.ts +45 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-deleted-not-in-sidebar.test.ts +44 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-detail-view.test.ts +46 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-edit-title.test.ts +48 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-expand-collapse.test.ts +50 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-list-in-sidebar.test.ts +37 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/areas-reorder-dnd.test.ts +82 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/auth-logout.test.ts +18 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/auth-reject-invalid-token.test.ts +35 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/auth-reject-used-token.test.ts +104 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/auth-request-magic-link.test.ts +29 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/auth-verify-via-pasted-token.test.ts +52 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/auth-verify-via-url.test.ts +48 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/auth.setup.ts +55 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/constants.ts +13 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/dnd-inbox-to-area.test.ts +74 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/dnd-inbox-to-project.test.ts +73 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/dnd-move-to-inbox.test.ts +51 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/dnd-project-to-area.test.ts +76 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/dnd-project-to-project.test.ts +75 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/home.test.ts +27 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-complete.test.ts +53 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-completed-not-in-sidebar.test.ts +46 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-create-in-area.test.ts +55 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-create-top-level.test.ts +37 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-delete.test.ts +55 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-edit-notes-date.test.ts +69 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-edit-title.test.ts +48 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-move-into-area.test.ts +72 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-move-out-of-area.test.ts +76 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-reorder-top-level.test.ts +82 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-reorder-within-area.test.ts +91 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-uncomplete.test.ts +39 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/projects-view-tasks.test.ts +48 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-complete-removes-from-active-views.test.ts +52 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-complete.test.ts +46 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-create-in-inbox.test.ts +19 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-create-in-project.test.ts +44 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-delete.test.ts +52 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-edit.test.ts +73 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-reorder-in-project.test.ts +81 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-reorder-inbox.test.ts +80 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-reorder-today.test.ts +103 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-uncomplete.test.ts +51 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/tasks-view-details.test.ts +51 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-active-sidebar-highlight.test.ts +38 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-inbox-unassigned-only.test.ts +43 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-navigate-to-completed.test.ts +59 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-navigate-to-inbox.test.ts +21 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-navigate-to-today.test.ts +42 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-navigate-to-upcoming.test.ts +59 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-sidebar-built-in-views.test.ts +23 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tests/views-today-shows-overdue.test.ts +48 -0
- package/skills/maestro-examples/references/setlist/setlist-web/tsconfig.json +22 -0
- package/skills/maestro-examples/references/setlist/setlist-web/vite.config.ts +37 -0
- package/skills/maestro-examples/references/setlist/specs/app.md +69 -0
- package/skills/maestro-examples/references/setlist/specs/data-models.md +92 -0
- package/skills/maestro-examples/references/setlist/specs/database-schema.md +76 -0
- package/skills/maestro-examples/references/setlist/specs/drag-and-drop.md +89 -0
- package/skills/maestro-examples/references/setlist/specs/magic-link-login.md +300 -0
- package/skills/maestro-examples/references/setlist/specs/position-ordering.md +329 -0
- package/skills/maestro-examples/references/setlist/specs/sidebar.md +65 -0
- package/skills/maestro-examples/references/setlist/specs/use-cases.md +55 -0
- package/skills/maestro-examples/references/setlist/tsconfig.json +28 -0
- package/{dist → skills}/maestro-packages/SKILL.md +3 -3
- package/skills/maestro-packages/permissions.md +215 -0
- package/skills/maestro-packages/queue.md +442 -0
- package/skills/maestro-packages/recurring-jobs.md +238 -0
- package/skills/react-use-effect/SKILL.md +65 -0
- package/skills/react-use-effect/references/patterns.md +283 -0
- package/dist/maestro-packages/queue.md +0 -284
- package/dist/maestro-packages/recurring-jobs.md +0 -353
- package/dist/maestro-packages/tracing.md +0 -240
- /package/{dist → skills}/maestro-cli/SKILL.md +0 -0
- /package/{dist → skills}/maestro-packages/broadcast.md +0 -0
- /package/{dist → skills}/maestro-packages/cache-control.md +0 -0
- /package/{dist → skills}/maestro-packages/cache.md +0 -0
- /package/{dist → skills}/maestro-packages/config.md +0 -0
- /package/{dist → skills}/maestro-packages/context.md +0 -0
- /package/{dist → skills}/maestro-packages/crypt.md +0 -0
- /package/{dist → skills}/maestro-packages/db.md +0 -0
- /package/{dist → skills}/maestro-packages/events.md +0 -0
- /package/{dist → skills}/maestro-packages/exceptions.md +0 -0
- /package/{dist → skills}/maestro-packages/form.md +0 -0
- /package/{dist → skills}/maestro-packages/hash.md +0 -0
- /package/{dist → skills}/maestro-packages/helpers.md +0 -0
- /package/{dist → skills}/maestro-packages/log.md +0 -0
- /package/{dist → skills}/maestro-packages/mail.md +0 -0
- /package/{dist → skills}/maestro-packages/metrics.md +0 -0
- /package/{dist → skills}/maestro-packages/rate-limiting.md +0 -0
- /package/{dist → skills}/maestro-packages/react-router-file-routes.md +0 -0
- /package/{dist → skills}/maestro-packages/sql.md +0 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: maestro-architecture
|
|
3
|
+
description: "Layered TypeScript architecture pattern for maestro-js backends. Covers Zod schemas (base model, extended views, request inputs), database providers (data access with SQL + Zod parsing), and use cases (z.function business logic with permissions). Use when scaffolding new domains, writing providers or use cases, or understanding the schema/provider/use-case layer conventions."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Layered TypeScript Architecture: Schemas, Providers, and Use Cases
|
|
7
|
+
|
|
8
|
+
A portable pattern for building type-safe, layered Node.js backends. This architecture uses Zod as the backbone for runtime validation and type inference, providers as a strict data access layer, and use cases as the business logic orchestration layer.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Table of Contents
|
|
13
|
+
|
|
14
|
+
1. [Architecture Overview](#1-architecture-overview)
|
|
15
|
+
2. [Layer 1: Zod Schemas (Type System)](#2-layer-1-zod-schemas)
|
|
16
|
+
3. [Layer 2: Database Providers (Data Access)](#3-layer-2-database-providers)
|
|
17
|
+
4. [Layer 3: Use Cases (Business Logic)](#4-layer-3-use-cases)
|
|
18
|
+
5. [Supporting Utilities](#5-supporting-utilities)
|
|
19
|
+
6. [How It All Connects](#6-how-it-all-connects)
|
|
20
|
+
7. [Adapting to Your Project](#7-adapting-to-your-project)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. Architecture Overview
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
Route Handler (Remix action / Express route)
|
|
28
|
+
│
|
|
29
|
+
▼
|
|
30
|
+
Use Case (z.function, permissions, orchestration)
|
|
31
|
+
│
|
|
32
|
+
▼
|
|
33
|
+
Provider (SQL queries or Api Calls, Zod parsing, data mapping)
|
|
34
|
+
│
|
|
35
|
+
▼
|
|
36
|
+
Database / API (depending on the type of the provider)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Principles:**
|
|
40
|
+
- Schemas are the single source of truth for types AND runtime validation
|
|
41
|
+
- Providers own ALL database / external api access — no raw SQL anywhere else
|
|
42
|
+
- Use cases own ALL business logic — routes are thin wrappers
|
|
43
|
+
- Types flow from schemas through every layer via `z.infer<>`
|
|
44
|
+
- Hard dependencies everywhere — no DI containers, no mocks in production code
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 2. Layer 1: Zod Schemas
|
|
49
|
+
|
|
50
|
+
Schemas define every data shape in the system. They serve triple duty: TypeScript types, runtime validation, and documentation.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### 2a. Single-File Schema Pattern
|
|
54
|
+
|
|
55
|
+
Each domain entity lives in a single `index.ts` file. All three schema layers — base model, extended views, and request/response inputs — are defined together, then exported as a single namespace object with a companion `declare namespace` block for types.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// packages/schemas/src/widget-tags/index.ts
|
|
59
|
+
import { z } from 'zod'
|
|
60
|
+
import { isoZ } from '@maestro-js/iso-zod'
|
|
61
|
+
|
|
62
|
+
// ─── Enums ───────────────────────────────────────────────
|
|
63
|
+
const removedReasonOptions = ['expired', 'resolved', 'other'] as const
|
|
64
|
+
const removedReasonLabels = {
|
|
65
|
+
expired: 'Expired',
|
|
66
|
+
resolved: 'Resolved',
|
|
67
|
+
other: 'Other'
|
|
68
|
+
} satisfies { [key in (typeof removedReasonOptions)[number]]: string }
|
|
69
|
+
const removedReasonEnum = z.enum(removedReasonOptions).meta({ enumLabels: removedReasonLabels })
|
|
70
|
+
|
|
71
|
+
// ─── Layer A: Base Model (matches DB columns exactly) ────
|
|
72
|
+
const widgetTagModel = z.object({
|
|
73
|
+
id: z.uuid(),
|
|
74
|
+
widgetId: z.string(),
|
|
75
|
+
notes: z.string().max(2000).nullable(),
|
|
76
|
+
expirationDate: isoZ.date().nullable(),
|
|
77
|
+
createdDate: isoZ.instant(),
|
|
78
|
+
createdByProfileId: z.number(),
|
|
79
|
+
removedDate: isoZ.instant().nullable(),
|
|
80
|
+
removedByProfileId: z.number().nullable(),
|
|
81
|
+
removedReason: removedReasonEnum.nullable(),
|
|
82
|
+
removedNotes: z.string().max(2000).nullable(),
|
|
83
|
+
lastModifiedDate: isoZ.instant()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
// ─── Layer B: Extended Views (joined/computed fields) ────
|
|
87
|
+
const widgetTagAdminView = widgetTagModel.extend({
|
|
88
|
+
serialNumber: z.string().min(1).nullable(),
|
|
89
|
+
createdByUserName: z.string().nullable(),
|
|
90
|
+
removedByUserName: z.string().nullable()
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
// ─── Layer C: Request/Input Schemas ──────────────────────
|
|
94
|
+
const createWidgetTagInput = z.object({
|
|
95
|
+
widgetId: z.string().uuid(),
|
|
96
|
+
notes: z.string().max(2000).nullable(),
|
|
97
|
+
expirationDate: isoZ.date().nullable()
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// ─── Domain Helpers ──────────────────────────────────────
|
|
101
|
+
// Helpers reference the namespace type — works because declare namespace is hoisted.
|
|
102
|
+
function isTagActive(
|
|
103
|
+
tag: Pick<WidgetTagSchemas.widgetTagModel, 'removedDate'>
|
|
104
|
+
): boolean {
|
|
105
|
+
return tag.removedDate === null
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ─── Namespace Export ────────────────────────────────────
|
|
109
|
+
export const WidgetTagSchemas = {
|
|
110
|
+
removedReasonOptions,
|
|
111
|
+
removedReasonLabels,
|
|
112
|
+
removedReasonEnum,
|
|
113
|
+
widgetTagModel,
|
|
114
|
+
widgetTagAdminView,
|
|
115
|
+
createWidgetTagInput,
|
|
116
|
+
isTagActive
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ─── Type Declarations ───────────────────────────────────
|
|
120
|
+
// declare namespace merges with the exported const — consumers get
|
|
121
|
+
// both runtime schemas and inferred types from a single import.
|
|
122
|
+
export declare namespace WidgetTagSchemas {
|
|
123
|
+
export type widgetTagModel = z.infer<typeof widgetTagModel>
|
|
124
|
+
export type widgetTagAdminView = z.infer<typeof widgetTagAdminView>
|
|
125
|
+
export type createWidgetTagInput = z.infer<typeof createWidgetTagInput>
|
|
126
|
+
export type RemovedReason = (typeof removedReasonOptions)[number]
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Key points:**
|
|
131
|
+
- **`const` + `declare namespace` merge** — `WidgetTagSchemas.widgetTagModel` is a Zod schema at runtime and a type at compile time.
|
|
132
|
+
- **Only the final object is exported** — individual schemas are plain `const` declarations, keeping the public API surface explicit.
|
|
133
|
+
- **Domain helpers reference the namespace type** — `declare namespace` is hoisted, so helpers can use `Pick<WidgetTagSchemas.widgetTagModel, ...>` even though the declaration appears below.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { WidgetTagSchemas } from 'my-schemas-package'
|
|
137
|
+
|
|
138
|
+
const parsed = WidgetTagSchemas.createWidgetTagInput.parse(formData)
|
|
139
|
+
function renderTag(t: WidgetTagSchemas.widgetTagAdminView) { ... }
|
|
140
|
+
if (WidgetTagSchemas.isTagActive(tag)) { ... }
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
index.ts re-export each entity's namespace — one line per entity:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
// packages/schemas/src/index.ts
|
|
147
|
+
export { WidgetTagSchemas } from './widget-tags'
|
|
148
|
+
export { WidgetSchemas } from './widget'
|
|
149
|
+
export { WidgetMetricSchemas } from './widget-metric'
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### 2b. Enum Pattern
|
|
153
|
+
|
|
154
|
+
Every enum uses a three-part structure. The options array is always the source of truth — never define values inside `z.enum()` directly.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const priceUnitOptions = ['Hourly', 'Daily', 'Total'] as const // 1. options array (source of truth)
|
|
158
|
+
const priceUnits = enumOptionsToObject(priceUnitOptions) // 2. object for runtime lookups
|
|
159
|
+
type PriceUnit = (typeof priceUnitOptions)[number] // 3. TypeScript type
|
|
160
|
+
|
|
161
|
+
// Labels layered on as needed
|
|
162
|
+
const priceUnitLabels = { Hourly: 'Per Hour', Daily: 'Per Day', Total: 'Fixed Price' } satisfies Record<PriceUnit, string>
|
|
163
|
+
```
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 3. Layer 2: Database Providers
|
|
167
|
+
|
|
168
|
+
Providers encapsulate ALL database access. They translate between SQL and typed domain objects using Zod schemas.
|
|
169
|
+
|
|
170
|
+
**Providers are a private interface.** They must never be exported from the services package. External consumers (routes, other packages) access data exclusively through use cases. Only use cases may import and call providers directly.
|
|
171
|
+
|
|
172
|
+
### 3a. Structure
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// packages/services/src/categories/category-db-data-provider.ts
|
|
176
|
+
import { instantFns } from 'iso-fns2'
|
|
177
|
+
import { CategorySchemas } from 'my-schemas-package'
|
|
178
|
+
import { NotFoundError } from '@maestro-js/custom-errors'
|
|
179
|
+
import { nonNullAsync } from '../../internals'
|
|
180
|
+
import { Db } from 'maestro'
|
|
181
|
+
|
|
182
|
+
// --- Mapping function: DB row → typed domain object ---
|
|
183
|
+
function mapToCategory(raw: any) {
|
|
184
|
+
return CategorySchemas.categorySystemView.parse({
|
|
185
|
+
...raw,
|
|
186
|
+
isDeleted: !!raw.isDeleted
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// --- Query functions ---
|
|
191
|
+
async function listByOrganizationId(organizationId: number) {
|
|
192
|
+
const results = await Db.select(
|
|
193
|
+
`SELECT * FROM categories WHERE organizationId = ?`,
|
|
194
|
+
[organizationId]
|
|
195
|
+
)
|
|
196
|
+
return results.map(mapToCategory)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function getById(id: number) {
|
|
200
|
+
const results = await Db.select(
|
|
201
|
+
`SELECT * FROM categories WHERE id = ?`,
|
|
202
|
+
[id]
|
|
203
|
+
)
|
|
204
|
+
return results.length ? mapToCategory(results[0]) : null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Strict variant — throws NotFoundError instead of returning null
|
|
208
|
+
const getByIdStrict = nonNullAsync(getById)
|
|
209
|
+
|
|
210
|
+
async function create(category: CategorySchemas.categoryUpdate) {
|
|
211
|
+
return await Db.transaction(async () => {
|
|
212
|
+
const insert = await Db.insert(
|
|
213
|
+
`INSERT INTO categories SET ?`,
|
|
214
|
+
[{ ...category, createdDate: instantFns.formatISO9075(instantFns.now()) }]
|
|
215
|
+
)
|
|
216
|
+
return getByIdStrict(insert.insertId)
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function deleteById(id: number) {
|
|
221
|
+
await Db.update(
|
|
222
|
+
`UPDATE categories SET deletedDate = ? WHERE id = ?`,
|
|
223
|
+
[instantFns.formatISO9075(instantFns.now()), id]
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function hardDeleteById(id: number) {
|
|
228
|
+
await Db.delete(`DELETE FROM categories WHERE id = ?`, [id])
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// --- Export as plain object literal ---
|
|
232
|
+
export const CategoryDbDataProvider = {
|
|
233
|
+
listByOrganizationId,
|
|
234
|
+
getById,
|
|
235
|
+
getByIdStrict,
|
|
236
|
+
create,
|
|
237
|
+
deleteById,
|
|
238
|
+
hardDeleteById
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### 3b. Naming Conventions (Strict)
|
|
243
|
+
|
|
244
|
+
| Prefix | Returns | Behavior |
|
|
245
|
+
| -------------- | ----------- | ------------------------------------- |
|
|
246
|
+
| `get[X]` | `T \| null` | Single item or null |
|
|
247
|
+
| `get[X]Strict` | `T` | Single item or throws `NotFoundError` |
|
|
248
|
+
| `list[X]` | `T[]` | Array (empty array if none) |
|
|
249
|
+
| `create` | `T` | INSERT |
|
|
250
|
+
| `update` | `T` | UPDATE |
|
|
251
|
+
| `delete` | `void` | Soft delete (sets `deletedDate`) |
|
|
252
|
+
| `hardDelete` | `void` | Permanent DELETE |
|
|
253
|
+
|
|
254
|
+
### 3c. Query Builder for Dynamic Filters
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { Sql } from '@maestro-js/sql'
|
|
258
|
+
|
|
259
|
+
async function listByFilter(filter: EntityFilter) {
|
|
260
|
+
const where = Sql.where()
|
|
261
|
+
|
|
262
|
+
if (filter.organizationId) {
|
|
263
|
+
where.and('organizationId = ?', [filter.organizationId])
|
|
264
|
+
}
|
|
265
|
+
if (filter.isArchived !== undefined) {
|
|
266
|
+
where.and('isArchived = ?', [filter.isArchived])
|
|
267
|
+
}
|
|
268
|
+
if (!filter.includeDeleted) {
|
|
269
|
+
where.and('deletedDate IS NULL')
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const limit = filter.limit ?? 50
|
|
273
|
+
const results = await Db.select(
|
|
274
|
+
`SELECT * FROM widgets WHERE ${where.query} ORDER BY createdDate DESC LIMIT ?`,
|
|
275
|
+
[...where.parameters, limit]
|
|
276
|
+
)
|
|
277
|
+
return results.map(mapToWidget)
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`Sql.where()` creates a `WhereClauseBuilder` for constructing parameterized WHERE clauses incrementally. It provides:
|
|
282
|
+
- `and(condition, params?)` — appends an AND clause (or a parenthesized group when given a callback)
|
|
283
|
+
- `or(condition, params?)` — appends an OR clause (or a parenthesized group when given a callback)
|
|
284
|
+
- `.query` — getter for the assembled WHERE clause string (returns `'TRUE'` if no conditions added)
|
|
285
|
+
- `.parameters` — getter for the parameter array matching `?` placeholders
|
|
286
|
+
|
|
287
|
+
### 3d. Rules
|
|
288
|
+
|
|
289
|
+
1. **All SQL lives in providers** — use cases and routes never write SQL
|
|
290
|
+
2. **Always parse results through Zod** — `Schema.parse(dbRow)` ensures runtime type safety
|
|
291
|
+
3. **Use `nonNullAsync` for strict variants** — avoids null-check boilerplate in callers
|
|
292
|
+
4. **Use transactions for multi-step writes** — `Db.transaction(async () => { ... })`
|
|
293
|
+
5. **Soft delete by default** — set `deletedDate`, filter it in queries. Use `hardDelete` only when truly needed
|
|
294
|
+
6. **Check array length before IN queries** — `if (!ids.length) return []` to avoid SQL syntax errors
|
|
295
|
+
7. **Never export providers from the services package** — providers are internal to the services layer. Routes and other external consumers must go through use cases. Only use cases may import providers
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 4. Layer 3: Use Cases
|
|
300
|
+
|
|
301
|
+
Use cases are the business logic layer. Each use case represents one complete user action and orchestrates providers, permissions, and side effects.
|
|
302
|
+
|
|
303
|
+
### 4a. The Pattern: `z.function().implementAsync()`
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// packages/services/src/categories/category-use-case.ts
|
|
307
|
+
import { z } from 'zod'
|
|
308
|
+
import { CategorySchemas, UserSchemas } from 'my-schemas-package'
|
|
309
|
+
import { ForbiddenError, ConflictError } from '@maestro-js/custom-errors'
|
|
310
|
+
import { CategoryDbDataProvider } from './category-db-data-provider'
|
|
311
|
+
|
|
312
|
+
export const ListCategoriesByOrganizationId = z
|
|
313
|
+
.function({ input: [z.number(), UserSchemas.Profile] })
|
|
314
|
+
.implementAsync(async (organizationId, profile) => {
|
|
315
|
+
// 1. Permission check
|
|
316
|
+
if (profile.clientType === 'ORG_USER' && !profile.organizationIds.includes(organizationId)) {
|
|
317
|
+
throw new ForbiddenError('Cannot list categories which are not yours')
|
|
318
|
+
}
|
|
319
|
+
// 2. Delegate to provider
|
|
320
|
+
return CategoryDbDataProvider.listByOrganizationId(organizationId)
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
export const CreateCategory = z
|
|
324
|
+
.function({ input: [CategorySchemas.categoryUpdate, UserSchemas.Profile] })
|
|
325
|
+
.implementAsync(async (category, profile) => {
|
|
326
|
+
if (profile.clientType === 'ORG_USER' && !profile.organizationIds.includes(category.organizationId)) {
|
|
327
|
+
throw new ForbiddenError('Cannot create categories which are not yours')
|
|
328
|
+
}
|
|
329
|
+
return CategoryDbDataProvider.create(category)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
export const DeleteCategory = z
|
|
333
|
+
.function({ input: [z.number(), UserSchemas.Profile] })
|
|
334
|
+
.implementAsync(async (id, profile) => {
|
|
335
|
+
const category = await CategoryDbDataProvider.getByIdStrict(id)
|
|
336
|
+
|
|
337
|
+
if (profile.clientType === 'ORG_USER' && !profile.organizationIds.includes(category.organizationId)) {
|
|
338
|
+
throw new ForbiddenError('Cannot delete categories which are not yours')
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Business rule: can't delete if still referenced
|
|
342
|
+
const [products, accessories] = await Promise.all([
|
|
343
|
+
ProductDbDataProvider.listByCategoryId(category.id),
|
|
344
|
+
AccessoryDbProvider.listPublishedByCategoryId(category.id)
|
|
345
|
+
])
|
|
346
|
+
|
|
347
|
+
if (products.length || accessories.length) {
|
|
348
|
+
throw new ConflictError('Cannot delete a category still associated with a product')
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
await CategoryDbDataProvider.deleteById(id)
|
|
352
|
+
})
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 4b. Permission Checks with Permissions.gate()
|
|
356
|
+
|
|
357
|
+
For RBAC-style permission checks, use `Permissions.gate()` at the top of each use case:
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import { Permissions } from '@maestro-js/permissions'
|
|
361
|
+
import { ForbiddenError } from '@maestro-js/custom-errors'
|
|
362
|
+
|
|
363
|
+
export const UpdateWidget = z
|
|
364
|
+
.function({ input: [z.string(), WidgetSchemas.updateWidgetRequest, UserSchemas.Profile] })
|
|
365
|
+
.implementAsync(async (id, data, profile) => {
|
|
366
|
+
// Throws ForbiddenError if user can't update widgets
|
|
367
|
+
Permissions.gate(profile, 'widgets', 'update')
|
|
368
|
+
|
|
369
|
+
// Additional resource-level check
|
|
370
|
+
const existing = await widgetDbProvider.getByIdStrict(id)
|
|
371
|
+
if (profile.clientType === 'ORG_USER' && !profile.organizationIds.includes(existing.organizationId)) {
|
|
372
|
+
throw new ForbiddenError('Cannot update widgets which are not yours')
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return widgetDbProvider.update(id, data)
|
|
376
|
+
})
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
The `Permissions` package follows the maestro service registry pattern. Set it up once at application boot:
|
|
380
|
+
|
|
381
|
+
```typescript
|
|
382
|
+
// app/boot.ts (or equivalent startup file)
|
|
383
|
+
import { Permissions } from '@maestro-js/permissions'
|
|
384
|
+
|
|
385
|
+
const driver = Permissions.drivers.static<UserSchemas.Profile>((profile, allow, forbid) => {
|
|
386
|
+
if (profile.role === 'admin') {
|
|
387
|
+
allow('widgets', ['create', 'read', 'update', 'delete'])
|
|
388
|
+
allow('categories', ['create', 'read', 'update', 'delete'])
|
|
389
|
+
}
|
|
390
|
+
if (profile.role === 'member') {
|
|
391
|
+
allow('widgets', ['create', 'read', 'update'])
|
|
392
|
+
allow('categories', 'read')
|
|
393
|
+
}
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
const service = Permissions.Provider.create({ driver })
|
|
397
|
+
Permissions.Provider.register('default', service)
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 4c. Exporting Use Cases as Services
|
|
401
|
+
|
|
402
|
+
Group related use cases into a service export:
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
// packages/services/src/categories/index.ts
|
|
406
|
+
export * as CategoryService from './category-use-case'
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Routes call individual use cases through the service namespace:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
// Express
|
|
413
|
+
routes.get('/:id', async (req, res) => {
|
|
414
|
+
const category = await CategoryService.GetCategoryById(Number(req.params.id), res.locals.profile)
|
|
415
|
+
res.json(category)
|
|
416
|
+
})
|
|
417
|
+
|
|
418
|
+
// Remix
|
|
419
|
+
export const action = async ({ request }: ActionFunctionArgs) => {
|
|
420
|
+
const profile = await getProfileStrict(request)
|
|
421
|
+
const formData = await request.formData()
|
|
422
|
+
const data = CategorySchemas.categoryUpdate.parse(Object.fromEntries(formData))
|
|
423
|
+
await CategoryService.CreateCategory(data, profile)
|
|
424
|
+
return redirect('/categories')
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### 4d. Rules
|
|
429
|
+
|
|
430
|
+
1. **Always use `z.function().implementAsync()`** — provides input validation and type inference
|
|
431
|
+
2. **Exactly two parameters** — use cases always accept exactly two inputs: the domain data as the first parameter and `UserSchemas.Profile` as the second parameter. Never add a third parameter; combine related domain inputs into a single object if needed
|
|
432
|
+
3. **Profile is always the second parameter** — every use case receives the actor's profile as its second argument, keeping business input cleanly separated from auth context
|
|
433
|
+
4. **Always check permissions first** — via `Permissions.gate()` or inline checks
|
|
434
|
+
5. **Never write raw SQL** — delegate to providers
|
|
435
|
+
6. **One use case = one complete user action** — the front-end calls ONE use case, not many
|
|
436
|
+
7. **Large use cases are fine** — prefer one comprehensive use case over fragmenting a workflow across multiple
|
|
437
|
+
8. **Hard dependencies only** — import providers directly, no dependency injection or mocking
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 5. Supporting Utilities
|
|
442
|
+
|
|
443
|
+
### 5a. `nonNullAsync`
|
|
444
|
+
|
|
445
|
+
Wraps an async function so it throws instead of returning null:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
import { NotFoundError } from '@maestro-js/custom-errors'
|
|
449
|
+
|
|
450
|
+
export function nonNullAsync<T extends (...args: any) => any>(
|
|
451
|
+
func: T
|
|
452
|
+
): (...args: Parameters<T>) => Promise<Exclude<Awaited<ReturnType<T>>, null>> {
|
|
453
|
+
return async function (...args: any) {
|
|
454
|
+
const res = await func(...args)
|
|
455
|
+
if (res === null) throw new NotFoundError()
|
|
456
|
+
return res
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Usage in a provider:
|
|
461
|
+
async function getById(id: number) { ... } // returns T | null
|
|
462
|
+
const getByIdStrict = nonNullAsync(getById) // returns T or throws
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### 5b. `@maestro-js/custom-errors`
|
|
466
|
+
|
|
467
|
+
Provides pre-built error classes with HTTP status codes for common failure modes. Each error carries an `httpStatusCode` property that the route layer uses to map to the correct HTTP response.
|
|
468
|
+
|
|
469
|
+
**Pre-built errors used in this architecture:**
|
|
470
|
+
|
|
471
|
+
| Error | HTTP Status | Used For |
|
|
472
|
+
| ----------------- | ----------- | ------------------------------------------------------------- |
|
|
473
|
+
| `NotFoundError` | 404 | Entity not found (thrown by `nonNullAsync`, strict providers) |
|
|
474
|
+
| `ForbiddenError` | 403 | Unauthorized resource access (inline ownership checks) |
|
|
475
|
+
| `ConflictError` | 409 | Business rule violations (e.g., deleting referenced entities) |
|
|
476
|
+
| `BadRequestError` | 400 | Invalid input that passes Zod but fails a business rule |
|
|
477
|
+
|
|
478
|
+
**Route-layer error handling:**
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
import { CustomErrors } from '@maestro-js/custom-errors'
|
|
482
|
+
|
|
483
|
+
app.use((err, req, res, next) => {
|
|
484
|
+
const status = CustomErrors.getHttpStatusCode(err) ?? 500
|
|
485
|
+
res.status(status).json({ error: err.message })
|
|
486
|
+
})
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**Creating domain-specific errors with `CustomErrors.create()`:**
|
|
490
|
+
|
|
491
|
+
For errors that need structured input or domain-specific names, use the factory:
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
import { CustomErrors } from '@maestro-js/custom-errors'
|
|
495
|
+
|
|
496
|
+
const InsufficientInventoryError = CustomErrors.create({
|
|
497
|
+
name: 'InsufficientInventoryError',
|
|
498
|
+
message: (input: { requested: number; available: number }) =>
|
|
499
|
+
`Requested ${input.requested} but only ${input.available} available`,
|
|
500
|
+
httpStatusCode: 'conflict' as const
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
// Usage
|
|
504
|
+
throw new InsufficientInventoryError({ requested: 10, available: 3 })
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
This is how `@maestro-js/permissions` defines its `PermissionsError` — a domain-specific forbidden error with structured `action` and `subject` attributes.
|
|
508
|
+
|
|
509
|
+
---
|
|
510
|
+
|
|
511
|
+
## 6. How It All Connects
|
|
512
|
+
|
|
513
|
+
### Directory Structure
|
|
514
|
+
|
|
515
|
+
```
|
|
516
|
+
packages/
|
|
517
|
+
├── schemas/ # All domain types and validation
|
|
518
|
+
│ └── src/
|
|
519
|
+
│ ├── widget/
|
|
520
|
+
│ │ ├── widget-model.ts # Layer A: DB columns
|
|
521
|
+
│ │ ├── widget-system-view.ts # Layer B: extended for API
|
|
522
|
+
│ │ ├── create-widget-request.ts # Layer C: mutation input
|
|
523
|
+
│ │ ├── update-widget-request.ts
|
|
524
|
+
│ │ ├── widget-admin-view.ts
|
|
525
|
+
│ │ └── index.ts # Barrel export
|
|
526
|
+
│ ├── user/
|
|
527
|
+
│ │ └── profile.ts # UserSchemas.Profile
|
|
528
|
+
│ └── index.ts # Top-level barrel
|
|
529
|
+
└── services/ # Providers + Use Cases
|
|
530
|
+
└── src/
|
|
531
|
+
└── widget/
|
|
532
|
+
├── widget-db-provider.ts # Data access (internal — never exported)
|
|
533
|
+
├── widget-use-case.ts # Business logic (public via index.ts)
|
|
534
|
+
├── widget-use-case.test.ts # Tests (no mocks)
|
|
535
|
+
└── index.ts # export * as WidgetService (use cases only)
|
|
536
|
+
└── utils/
|
|
537
|
+
└── make-non-null.ts # nonNullAsync
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Request Lifecycle
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
1. Route receives HTTP request
|
|
544
|
+
2. Route extracts user profile from auth middleware
|
|
545
|
+
3. Route calls: CategoryService.CreateCategory(parsedBody, profile)
|
|
546
|
+
4. Use case validates inputs via z.function() args
|
|
547
|
+
5. Use case calls Permissions.gate(profile, 'categories', 'create')
|
|
548
|
+
6. Use case calls CategoryDbDataProvider.create(category)
|
|
549
|
+
7. Provider runs SQL INSERT inside a transaction
|
|
550
|
+
8. Provider calls Schema.parse(dbRow) on the result
|
|
551
|
+
9. Typed result flows back up through use case → route → HTTP response
|
|
552
|
+
```
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: maestro-examples
|
|
3
|
+
description: "Three complete reference applications built with @maestro-js. Use when building a new app, scaffolding features, writing routes/components, setting up infrastructure, or needing a working example of any maestro pattern. Covers authentication (password and magic link), permissions, multi-tenancy, file uploads, queues, recurring jobs, email templates, drag-and-drop, and full-stack React Router + maestro integration."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Maestro Example Applications
|
|
7
|
+
|
|
8
|
+
Three production-style reference apps demonstrating how to build full-stack applications with @maestro-js and React Router. Each app is a complete monorepo with schemas, services, web frontend, configuration, infrastructure, and tests.
|
|
9
|
+
|
|
10
|
+
## When to read these references
|
|
11
|
+
|
|
12
|
+
- Scaffolding a new app or feature — read the closest app's full structure
|
|
13
|
+
- Writing a specific layer (schema, provider, use case, route) — read that file in any app
|
|
14
|
+
- Setting up maestro services — read any app's `packages/services/src/maestro.ts`
|
|
15
|
+
- Writing migrations — read any app's `infrastructure/db-migrations/sqls/`
|
|
16
|
+
- Writing e2e tests — read any app's `*-web/tests/`
|
|
17
|
+
- Setting up a queue processor — read setlist's `queue-processor/`
|
|
18
|
+
- Writing email templates — read chorus or setlist's `packages/email-templates/`
|
|
19
|
+
|
|
20
|
+
## App Overview
|
|
21
|
+
|
|
22
|
+
### Cadence — Multi-tenant CRM
|
|
23
|
+
`references/cadence/`
|
|
24
|
+
|
|
25
|
+
A contact management system with organizations, users, contacts, interactions, and tags.
|
|
26
|
+
|
|
27
|
+
**Unique patterns:**
|
|
28
|
+
- Multi-tenant data isolation (org-scoped queries)
|
|
29
|
+
- Password authentication with remember-me tokens
|
|
30
|
+
- Role-based permissions via `@maestro-js/permissions` (admin/member/viewer)
|
|
31
|
+
- Rich text editor for interaction notes
|
|
32
|
+
- Server-side table with filtering, sorting, and pagination
|
|
33
|
+
- Tag autocomplete system
|
|
34
|
+
|
|
35
|
+
**Key files:**
|
|
36
|
+
- `packages/schemas/src/` — 4 domain schemas (contact, interaction, organization, user)
|
|
37
|
+
- `packages/services/src/maestro.ts` — Full boot with Password, Auth, Permissions, Mail (SES)
|
|
38
|
+
- `packages/services/src/permissions.ts` — RBAC permission rules
|
|
39
|
+
- `packages/services/src/contact/` — Canonical provider + use case example
|
|
40
|
+
- `cadence-web/app/routes/contacts.tsx` — Server table with filters
|
|
41
|
+
- `cadence-web/app/routes/contacts.new.tsx` — Form action pattern
|
|
42
|
+
- `cadence-web/app/components/table/` — Reusable table system
|
|
43
|
+
- `cadence-web/app/features/` — Middleware (CSRF, rate limiting, security headers)
|
|
44
|
+
- `config/` — Per-service configuration files
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
### Chorus — Community Discussion Platform
|
|
49
|
+
`references/chorus/`
|
|
50
|
+
|
|
51
|
+
A Reddit/forum-style app with channels, posts, replies, likes, and image uploads.
|
|
52
|
+
|
|
53
|
+
**Unique patterns:**
|
|
54
|
+
- File uploads to S3 (`@maestro-js/file-storage`)
|
|
55
|
+
- Image grid display (Twitter-style 1-4 image layouts)
|
|
56
|
+
- Like/unlike toggle
|
|
57
|
+
- Content moderation (hiding posts, moderator roles)
|
|
58
|
+
- Channel ownership and transfer
|
|
59
|
+
- Email templates with React (password reset)
|
|
60
|
+
- Nested route layouts (`c.$slug/` with `_layout.tsx`)
|
|
61
|
+
|
|
62
|
+
**Key files:**
|
|
63
|
+
- `packages/schemas/src/` — 4 domain schemas (channel, post, like, profile)
|
|
64
|
+
- `packages/services/src/maestro.ts` — Boot with file storage (S3) in addition to standard services
|
|
65
|
+
- `packages/services/src/post/redact-hidden-post.ts` — Content filtering pattern
|
|
66
|
+
- `packages/services/src/channel/channel-use-cases.ts` — Ownership/moderator permission checks
|
|
67
|
+
- `chorus-web/app/routes/c.$slug/` — Nested route layout pattern
|
|
68
|
+
- `chorus-web/app/routes/api/like.$postId.tsx` — Fetcher action (no navigation)
|
|
69
|
+
- `chorus-web/app/components/post-composer.tsx` — File upload component
|
|
70
|
+
- `chorus-web/app/components/image-grid.tsx` — Responsive image layout
|
|
71
|
+
- `packages/email-templates/` — React email template system
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
### Setlist — Personal Task Manager
|
|
76
|
+
`references/setlist/`
|
|
77
|
+
|
|
78
|
+
A Things-style task manager with areas, projects, tasks, drag-and-drop reordering, and a background queue processor.
|
|
79
|
+
|
|
80
|
+
**Unique patterns:**
|
|
81
|
+
- Magic link authentication (no passwords)
|
|
82
|
+
- Background queue processor (separate Node.js service)
|
|
83
|
+
- Recurring jobs (cron-scheduled cleanup)
|
|
84
|
+
- Drag-and-drop reordering with fractional position tracking
|
|
85
|
+
- Inline task creation
|
|
86
|
+
- Single-user model (no multi-tenancy or roles)
|
|
87
|
+
- API-only routes (fetcher actions, no loaders)
|
|
88
|
+
|
|
89
|
+
**Key files:**
|
|
90
|
+
- `packages/schemas/src/` — 5 domain schemas (area, project, task, user, magic-link) + `position.ts` utility
|
|
91
|
+
- `packages/services/src/maestro.ts` — Boot with magic link auth (no Password provider)
|
|
92
|
+
- `packages/services/src/auth/verify-magic-link-credentials.ts` — Custom credential verifier
|
|
93
|
+
- `packages/services/src/task/task-db-provider.ts` — Position-based reordering queries
|
|
94
|
+
- `queue-processor/src/index.ts` — Queue worker with graceful shutdown
|
|
95
|
+
- `queue-processor/src/recurring-jobs.ts` — Cron job definitions
|
|
96
|
+
- `setlist-web/app/routes/api/` — API-only action routes (tasks, projects, areas CRUD + reorder)
|
|
97
|
+
- `setlist-web/app/components/task-list/` — DnD task list with keyboard support
|
|
98
|
+
- `setlist-web/app/components/sidebar/` — DnD sidebar with sortable areas/projects
|
|
99
|
+
- `setlist-web/app/hooks/` — Action hooks (use-task-actions, use-area-actions, use-project-actions)
|
|
100
|
+
- `packages/email-templates/` — Magic link email template
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Common Structure
|
|
105
|
+
|
|
106
|
+
All three apps share this monorepo layout:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
app-name/
|
|
110
|
+
├── package.json + tsconfig.json
|
|
111
|
+
├── config/ # Per-service env config
|
|
112
|
+
├── infrastructure/
|
|
113
|
+
│ ├── cdk/ # AWS CDK stack
|
|
114
|
+
│ └── db-migrations/
|
|
115
|
+
│ ├── *.ts # Migration runner files
|
|
116
|
+
│ └── sqls/*.sql # Up/down SQL scripts
|
|
117
|
+
├── packages/
|
|
118
|
+
│ ├── schemas/src/ # Zod schemas (model, views, inputs)
|
|
119
|
+
│ ├── services/src/
|
|
120
|
+
│ │ ├── maestro.ts # Service boot/registration
|
|
121
|
+
│ │ ├── permissions.ts # Permission rules (if applicable)
|
|
122
|
+
│ │ └── [domain]/
|
|
123
|
+
│ │ ├── [domain]-db-provider.ts
|
|
124
|
+
│ │ ├── [domain]-use-cases.ts
|
|
125
|
+
│ │ └── index.ts
|
|
126
|
+
│ └── email-templates/src/ # React email templates (chorus, setlist)
|
|
127
|
+
├── [app]-web/
|
|
128
|
+
│ ├── app/
|
|
129
|
+
│ │ ├── root.tsx
|
|
130
|
+
│ │ ├── routes.ts
|
|
131
|
+
│ │ ├── services.server.ts # Re-exports services for route imports
|
|
132
|
+
│ │ ├── routes/ # React Router routes (loaders + actions)
|
|
133
|
+
│ │ ├── components/ # UI components
|
|
134
|
+
│ │ ├── features/ # Server middleware
|
|
135
|
+
│ │ └── utils/ # Client utilities and hooks
|
|
136
|
+
│ └── tests/ # Playwright e2e tests
|
|
137
|
+
├── queue-processor/ # Background worker (setlist only)
|
|
138
|
+
├── specs/ # App requirements and data model docs
|
|
139
|
+
└── scripts/ # One-off utilities
|
|
140
|
+
```
|