create-m5kdev 0.41.0 → 0.41.1
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/package.json +1 -1
- package/templates/minimal-app/.cursor/rules/frontend-component-guide.mdc +95 -95
- package/templates/minimal-app/.cursor/rules/frontend-data-guide.mdc +103 -103
- package/templates/minimal-app/.cursor/rules/frontend-form-guide.mdc +80 -80
- package/templates/minimal-app/.cursor/rules/frontend-hook-guide.mdc +79 -79
- package/templates/minimal-app/.cursor/rules/frontend-i18n-guide.mdc +45 -45
- package/templates/minimal-app/.cursor/rules/module-dto-guide.mdc +60 -60
- package/templates/minimal-app/.cursor/rules/module-prompt-guide.mdc +214 -214
- package/templates/minimal-app/.cursor/rules/module-repository-guide.mdc +167 -167
- package/templates/minimal-app/.cursor/rules/module-schema-guide.mdc +53 -53
- package/templates/minimal-app/.cursor/rules/module-service-guide.mdc +367 -367
- package/templates/minimal-app/.cursor/rules/module-trpc-guide.mdc +71 -71
- package/templates/minimal-app/.github/workflows/check.yml.tpl +21 -0
- package/templates/minimal-app/AGENTS.md.tpl +22 -31
- package/templates/minimal-app/CLAUDE.md.tpl +15 -1
- package/templates/minimal-app/GLOSSARY.md.tpl +181 -0
- package/templates/minimal-app/README.md.tpl +2 -0
- package/templates/minimal-app/apps/.gitignore.tpl +13 -13
- package/templates/minimal-app/apps/e2e/package.json.tpl +23 -23
- package/templates/minimal-app/apps/email/package.json.tpl +34 -34
- package/templates/minimal-app/apps/expo/.gitignore.tpl +44 -44
- package/templates/minimal-app/apps/expo/.prettierignore +13 -13
- package/templates/minimal-app/apps/expo/.prettierrc.json.tpl +9 -9
- package/templates/minimal-app/apps/expo/CHANGELOG.md.tpl +156 -156
- package/templates/minimal-app/apps/expo/README.md.tpl +44 -44
- package/templates/minimal-app/apps/expo/expo-env.d.ts.tpl +3 -0
- package/templates/minimal-app/apps/expo/package.json.tpl +63 -63
- package/templates/minimal-app/apps/expo/uniwind-types.d.ts.tpl +10 -0
- package/templates/minimal-app/apps/landing/package.json.tpl +34 -34
- package/templates/minimal-app/apps/server/drizzle/0000_whole_echo.sql +366 -366
- package/templates/minimal-app/apps/server/drizzle/0002_crazy_lilith.sql +46 -46
- package/templates/minimal-app/apps/server/drizzle/0003_freezing_fantastic_four.sql +3 -3
- package/templates/minimal-app/apps/server/drizzle/0004_slow_ezekiel_stane.sql +98 -98
- package/templates/minimal-app/apps/server/drizzle/0005_wonderful_the_professor.sql +1 -1
- package/templates/minimal-app/apps/server/drizzle/0006_lazy_blue_marvel.sql +158 -158
- package/templates/minimal-app/apps/server/drizzle/0007_abnormal_ultimo.sql +1 -1
- package/templates/minimal-app/apps/server/drizzle/0008_steady_nekra.sql +86 -86
- package/templates/minimal-app/apps/server/drizzle/meta/0000_snapshot.json.tpl +2624 -2624
- package/templates/minimal-app/apps/server/drizzle/meta/0001_snapshot.json.tpl +2670 -2670
- package/templates/minimal-app/apps/server/drizzle/meta/0002_snapshot.json.tpl +2715 -2715
- package/templates/minimal-app/apps/server/drizzle/meta/0003_snapshot.json.tpl +2687 -2687
- package/templates/minimal-app/apps/server/drizzle/meta/0004_snapshot.json.tpl +2497 -2497
- package/templates/minimal-app/apps/server/drizzle/meta/0005_snapshot.json.tpl +2483 -2483
- package/templates/minimal-app/apps/server/drizzle/meta/0006_snapshot.json.tpl +3602 -3602
- package/templates/minimal-app/apps/server/drizzle/meta/0007_snapshot.json.tpl +3619 -3619
- package/templates/minimal-app/apps/server/drizzle/meta/0008_snapshot.json.tpl +3619 -3619
- package/templates/minimal-app/apps/server/drizzle/meta/_journal.json.tpl +76 -76
- package/templates/minimal-app/apps/server/package.json.tpl +57 -57
- package/templates/minimal-app/apps/server/src/app.ts.tpl +1 -0
- package/templates/minimal-app/apps/shared/package.json.tpl +27 -27
- package/templates/minimal-app/apps/webapp/AGENTS.md.tpl +24 -24
- package/templates/minimal-app/apps/webapp/index.html.tpl +22 -22
- package/templates/minimal-app/apps/webapp/package.json.tpl +53 -53
- package/templates/minimal-app/apps/webapp/public/logo.svg.tpl +6 -6
- package/templates/minimal-app/biome.json.tpl +1 -0
- package/templates/minimal-app/docs/adr/.gitkeep +0 -0
- package/templates/minimal-app/docs/agents/domain.md.tpl +38 -0
- package/templates/minimal-app/docs/agents/issue-tracker.md.tpl +45 -0
- package/templates/minimal-app/docs/agents/triage-labels.md.tpl +15 -0
- package/templates/minimal-app/pnpm-workspace.yaml.tpl +12 -12
- package/templates/minimal-app/template.manifest.json +12 -0
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
---
|
|
2
|
-
globs: apps/**/server/src/modules/**/*.trpc.ts
|
|
3
|
-
alwaysApply: false
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Module tRPC File Style Guide
|
|
7
|
-
|
|
8
|
-
Conventions for tRPC router files in app modules. Routers are thin wiring:
|
|
9
|
-
they attach the module DTO schemas and delegate to the service. The router is
|
|
10
|
-
also the type source for the client — the webapp infers all row/list types
|
|
11
|
-
from `AppRouter`, so accurate `.input()`/`.output()` here is what makes
|
|
12
|
-
end-to-end types work.
|
|
13
|
-
|
|
14
|
-
## File Location
|
|
15
|
-
|
|
16
|
-
```
|
|
17
|
-
apps/<app>/server/src/modules/<module>/<module>.trpc.ts
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## Conventions
|
|
21
|
-
|
|
22
|
-
- Export a factory function named `create<Module>TRPC`.
|
|
23
|
-
- Import the module DTO object (`postSchemas`) from `./<module>.dto` — not
|
|
24
|
-
shared schemas, not hand-written types.
|
|
25
|
-
- Use `handleTRPCResult` from `@m5kdev/backend/utils/trpc` to unwrap `Result`s.
|
|
26
|
-
- Keep resolvers one-liners that delegate to the service; no business logic or
|
|
27
|
-
DB access here.
|
|
28
|
-
- Always pass `ctx` to service methods as the second argument. The first
|
|
29
|
-
argument (`input`) can be `undefined` for optional-input queries.
|
|
30
|
-
|
|
31
|
-
## Factory Pattern
|
|
32
|
-
|
|
33
|
-
```typescript
|
|
34
|
-
import { handleTRPCResult, type TRPCMethods } from "@m5kdev/backend/utils/trpc";
|
|
35
|
-
import { postSchemas } from "./posts.dto";
|
|
36
|
-
import type { PostsService } from "./posts.service";
|
|
37
|
-
|
|
38
|
-
export function createPostsTRPC(
|
|
39
|
-
{ router, privateProcedure: procedure }: TRPCMethods,
|
|
40
|
-
postsService: PostsService
|
|
41
|
-
) {
|
|
42
|
-
return router({
|
|
43
|
-
list: procedure
|
|
44
|
-
.input(postSchemas.input.list)
|
|
45
|
-
.output(postSchemas.output.list)
|
|
46
|
-
.query(async ({ ctx, input }) => handleTRPCResult(await postsService.list(input ?? {}, ctx))),
|
|
47
|
-
|
|
48
|
-
create: procedure
|
|
49
|
-
.input(postSchemas.input.create)
|
|
50
|
-
.output(postSchemas.output.single)
|
|
51
|
-
.mutation(async ({ ctx, input }) => handleTRPCResult(await postsService.create(input, ctx))),
|
|
52
|
-
|
|
53
|
-
softDelete: procedure
|
|
54
|
-
.input(postSchemas.input.delete)
|
|
55
|
-
.output(postSchemas.output.uuid)
|
|
56
|
-
.mutation(async ({ ctx, input }) =>
|
|
57
|
-
handleTRPCResult(await postsService.softDelete(input, ctx))
|
|
58
|
-
),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Procedure Selection
|
|
64
|
-
|
|
65
|
-
- `privateProcedure` — authenticated user (most module routes).
|
|
66
|
-
- `organizationProcedure` — requires an active organization.
|
|
67
|
-
- `adminProcedure` — admin role.
|
|
68
|
-
- `publicProcedure` — unauthenticated; use sparingly.
|
|
69
|
-
|
|
70
|
-
The service's own `.requireAuth()`/`.access()` still run — the tRPC procedure
|
|
71
|
-
choice is transport-level gating, not a replacement for service checks.
|
|
1
|
+
---
|
|
2
|
+
globs: apps/**/server/src/modules/**/*.trpc.ts
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Module tRPC File Style Guide
|
|
7
|
+
|
|
8
|
+
Conventions for tRPC router files in app modules. Routers are thin wiring:
|
|
9
|
+
they attach the module DTO schemas and delegate to the service. The router is
|
|
10
|
+
also the type source for the client — the webapp infers all row/list types
|
|
11
|
+
from `AppRouter`, so accurate `.input()`/`.output()` here is what makes
|
|
12
|
+
end-to-end types work.
|
|
13
|
+
|
|
14
|
+
## File Location
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
apps/<app>/server/src/modules/<module>/<module>.trpc.ts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Conventions
|
|
21
|
+
|
|
22
|
+
- Export a factory function named `create<Module>TRPC`.
|
|
23
|
+
- Import the module DTO object (`postSchemas`) from `./<module>.dto` — not
|
|
24
|
+
shared schemas, not hand-written types.
|
|
25
|
+
- Use `handleTRPCResult` from `@m5kdev/backend/utils/trpc` to unwrap `Result`s.
|
|
26
|
+
- Keep resolvers one-liners that delegate to the service; no business logic or
|
|
27
|
+
DB access here.
|
|
28
|
+
- Always pass `ctx` to service methods as the second argument. The first
|
|
29
|
+
argument (`input`) can be `undefined` for optional-input queries.
|
|
30
|
+
|
|
31
|
+
## Factory Pattern
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { handleTRPCResult, type TRPCMethods } from "@m5kdev/backend/utils/trpc";
|
|
35
|
+
import { postSchemas } from "./posts.dto";
|
|
36
|
+
import type { PostsService } from "./posts.service";
|
|
37
|
+
|
|
38
|
+
export function createPostsTRPC(
|
|
39
|
+
{ router, privateProcedure: procedure }: TRPCMethods,
|
|
40
|
+
postsService: PostsService
|
|
41
|
+
) {
|
|
42
|
+
return router({
|
|
43
|
+
list: procedure
|
|
44
|
+
.input(postSchemas.input.list)
|
|
45
|
+
.output(postSchemas.output.list)
|
|
46
|
+
.query(async ({ ctx, input }) => handleTRPCResult(await postsService.list(input ?? {}, ctx))),
|
|
47
|
+
|
|
48
|
+
create: procedure
|
|
49
|
+
.input(postSchemas.input.create)
|
|
50
|
+
.output(postSchemas.output.single)
|
|
51
|
+
.mutation(async ({ ctx, input }) => handleTRPCResult(await postsService.create(input, ctx))),
|
|
52
|
+
|
|
53
|
+
softDelete: procedure
|
|
54
|
+
.input(postSchemas.input.delete)
|
|
55
|
+
.output(postSchemas.output.uuid)
|
|
56
|
+
.mutation(async ({ ctx, input }) =>
|
|
57
|
+
handleTRPCResult(await postsService.softDelete(input, ctx))
|
|
58
|
+
),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Procedure Selection
|
|
64
|
+
|
|
65
|
+
- `privateProcedure` — authenticated user (most module routes).
|
|
66
|
+
- `organizationProcedure` — requires an active organization.
|
|
67
|
+
- `adminProcedure` — admin role.
|
|
68
|
+
- `publicProcedure` — unauthenticated; use sparingly.
|
|
69
|
+
|
|
70
|
+
The service's own `.requireAuth()`/`.access()` still run — the tRPC procedure
|
|
71
|
+
choice is transport-level gating, not a replacement for service checks.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
check:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: pnpm/action-setup@v4
|
|
13
|
+
with:
|
|
14
|
+
version: 10.13.1
|
|
15
|
+
- uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 24
|
|
18
|
+
cache: pnpm
|
|
19
|
+
- run: pnpm install
|
|
20
|
+
- run: pnpm lint
|
|
21
|
+
- run: pnpm check-types
|
|
@@ -22,38 +22,29 @@
|
|
|
22
22
|
// m5k:expo:end
|
|
23
23
|
|
|
24
24
|
// m5k:server:start
|
|
25
|
-
##
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- Register modules in `apps/server/src/app.ts` via `createBackendApp(config, [modules])`.
|
|
29
|
-
- Import Kernel infrastructure (`BaseModule`, Grants, DTOs) from `@m5kdev/backend/base/*`. The `modules/base` path still re-exports.
|
|
30
|
-
- Clay, Docx, Pdf, Social, and Video are Optional Backend Module packages (`@m5kdev/module-<name>`). They are not scaffolded; add them only when the product needs them.
|
|
31
|
-
- Use `BaseModule` subclasses for app modules; the kernel wires repositories, services, and tRPC.
|
|
32
|
-
- Keep tRPC files focused on input/output wiring and delegate logic to services.
|
|
33
|
-
- Do not add service methods that merely call another service. Call the owning service directly unless the method adds a business rule, authorization, validation, orchestration, a transaction boundary, or meaningful domain translation.
|
|
34
|
-
- Renaming a dependency method, repackaging arguments, constructing a prompt, or forwarding actor/context data alone does not justify a service wrapper.
|
|
35
|
-
- Do not create Drizzle migrations by hand. Use the scaffolded config and your project migration workflow later if you need generated migrations.
|
|
36
|
-
- Server event: emit through Auth from Workflow job handlers (and similar background work) when the UI must refresh a change the triggering mutation did not return. See `.cursor/rules/server-event-emit.mdc`.
|
|
37
|
-
// m5k:server:end
|
|
25
|
+
## Modules
|
|
26
|
+
|
|
27
|
+
`posts` is the reference module. Mirror its layout when adding a feature:
|
|
38
28
|
|
|
29
|
+
- `apps/shared/src/modules/posts/`
|
|
30
|
+
- `apps/server/src/modules/posts/`
|
|
39
31
|
// m5k:webapp:start
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
- Keep the app shell shape: `NuqsAdapter` + `BrowserRouter` + `Providers`.
|
|
43
|
-
- Compose global providers only in `apps/webapp/src/Providers.tsx`.
|
|
44
|
-
- Use `AppConfigProvider` and `AppTrpcQueryProvider` from `@m5kdev/frontend`; use `@m5kdev/web-ui` for web-only UI.
|
|
45
|
-
- Define app roles in `apps/shared/src/modules/app/roles.constants.ts` and pass `roles: APP_ROLES_CONFIG` through `AppConfigProvider` and `createBackendApp` metadata.
|
|
46
|
-
- Add role display labels in `apps/webapp/translations/app.json` using keys like `organization.role.editor` (resolved as `app:organization.role.editor`).
|
|
47
|
-
- When adding custom roles, also extend module grants (for example `*.grants.ts`) so permissions match the new role keys.
|
|
48
|
-
- For upgrading existing apps, see the docs site guide `apps/docs/docs/guides/custom-app-roles-migration.md`.
|
|
49
|
-
- Use `nuqs` for URL state, React Router for routing, HeroUI for UI primitives, and Tailwind v4 for styling.
|
|
50
|
-
- Prefer shared framework utilities from `@m5kdev/frontend` and `@m5kdev/web-ui` before adding local duplicates.
|
|
51
|
-
- React 19 idioms: no `forwardRef` (pass `ref` as a prop), no `defaultProps`, use `startTransition`/`useDeferredValue` for low-priority updates.
|
|
52
|
-
- Prefer HeroUI v3 components over hand-rolled HTML + Tailwind whenever an equivalent exists; Tailwind is for layout and styling on top of them.
|
|
53
|
-
- Keep components display-focused: data fetching, mutations, URL state, and effect chains live in custom hooks under `modules/<feature>/hooks/` (small focused hooks composed into one screen-level hook, e.g. `usePostsRoute`). Ephemeral UI state (modal open, selected row) may stay in the component. Extract reusable tRPC actions (delete, publish) into their own hooks.
|
|
54
|
-
- Fetch all server data with TanStack Query over tRPC (`useTRPC()` + `queryOptions`/`mutationOptions`); invalidate with the matching `queryFilter()` after mutations. No `fetch`/axios.
|
|
55
|
-
- Forms are uncontrolled HeroUI `Form` + `TextField`/`TextArea` with native HTML validation (`isRequired`, `type`, `minLength`) and `FieldError`; read values from `FormData` on submit. Never add form libraries (react-hook-form, formik). Validation that HTML cannot express (e.g. confirm-password) happens in the submit handler.
|
|
56
|
-
- All user-facing copy goes through i18next (`useTranslation`); keys live in `apps/webapp/translations/`.
|
|
57
|
-
- The `posts` feature (`apps/webapp/src/modules/posts/`) is the reference implementation for these conventions; see `.cursor/rules/frontend-*.mdc` for the detailed guides.
|
|
32
|
+
- `apps/webapp/src/modules/posts/`
|
|
58
33
|
// m5k:webapp:end
|
|
59
34
|
|
|
35
|
+
Read the matching `.cursor/rules` guide before editing a module file.
|
|
36
|
+
// m5k:server:end
|
|
37
|
+
|
|
38
|
+
## Agent skills
|
|
39
|
+
|
|
40
|
+
### Issue tracker
|
|
41
|
+
|
|
42
|
+
Issues live in GitHub Issues. Use the `gh` CLI. See `docs/agents/issue-tracker.md`.
|
|
43
|
+
|
|
44
|
+
### Triage labels
|
|
45
|
+
|
|
46
|
+
Default role labels: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See `docs/agents/triage-labels.md`.
|
|
47
|
+
|
|
48
|
+
### Domain docs
|
|
49
|
+
|
|
50
|
+
Single-context: one root `GLOSSARY.md` and `docs/adr/`. See `docs/agents/domain.md`.
|
|
@@ -139,6 +139,20 @@ The framework's detailed conventions live in `.cursor/rules/*.mdc`. Cursor appli
|
|
|
139
139
|
| `.cursor/rules/frontend-i18n-guide.mdc` | webapp user-facing copy |
|
|
140
140
|
// m5k:webapp:end
|
|
141
141
|
|
|
142
|
-
`AGENTS.md` (repo root and per-app)
|
|
142
|
+
`AGENTS.md` (repo root and per-app) points at this file and `.cursor/rules`. This file plus the guides above are the authoritative conventions.
|
|
143
143
|
// m5k:server:end
|
|
144
144
|
|
|
145
|
+
## Agent skills
|
|
146
|
+
|
|
147
|
+
### Issue tracker
|
|
148
|
+
|
|
149
|
+
Issues live in GitHub Issues. Use the `gh` CLI. See `docs/agents/issue-tracker.md`.
|
|
150
|
+
|
|
151
|
+
### Triage labels
|
|
152
|
+
|
|
153
|
+
Default role labels: `needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`. See `docs/agents/triage-labels.md`.
|
|
154
|
+
|
|
155
|
+
### Domain docs
|
|
156
|
+
|
|
157
|
+
Single-context: one root `GLOSSARY.md` and `docs/adr/`. See `docs/agents/domain.md`.
|
|
158
|
+
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# {{APP_NAME}}
|
|
2
|
+
|
|
3
|
+
{{APP_DESCRIPTION}}
|
|
4
|
+
|
|
5
|
+
## Language
|
|
6
|
+
# m5k:server:start
|
|
7
|
+
|
|
8
|
+
### Tenancy
|
|
9
|
+
|
|
10
|
+
**Organization**:
|
|
11
|
+
The default tenancy unit. Signup creates one; a Closed Organization cannot be used.
|
|
12
|
+
_Avoid_: Workspace, tenant, account, company, Team
|
|
13
|
+
|
|
14
|
+
**Membership**:
|
|
15
|
+
A durable seat in an Organization. Invite can create it before a User exists; leave, cancel, and expiry soft-delete it; rejoin revives the same MemberId. An Organization has exactly one Owner.
|
|
16
|
+
_Avoid_: OrgUser; treating Invitation as the Membership; Seat (that is Seat billing)
|
|
17
|
+
|
|
18
|
+
**Member**:
|
|
19
|
+
The Membership principal used to attribute and authorize org-scoped assets, including invited Memberships that do not yet have a User.
|
|
20
|
+
_Avoid_: User (when you mean the membership), Invitation
|
|
21
|
+
|
|
22
|
+
**MemberId**:
|
|
23
|
+
The Membership id stamped on org-scoped rows. In organization context, `"own"` grants compare this, not UserId.
|
|
24
|
+
_Avoid_: UserId as the org ownership key
|
|
25
|
+
|
|
26
|
+
**User**:
|
|
27
|
+
The global sign-in identity. Owns personal resources that are not org tenancy. A Closed User cannot sign in.
|
|
28
|
+
_Avoid_: Account, Customer, Client, Member
|
|
29
|
+
|
|
30
|
+
**UserId**:
|
|
31
|
+
The User id. Correct key for personal resources. Not the org-scoped ownership key.
|
|
32
|
+
_Avoid_: MemberId
|
|
33
|
+
|
|
34
|
+
**Owner**:
|
|
35
|
+
The Organization Role `owner`. Exactly one per Organization. Invite and org self-service cannot grant or transfer it.
|
|
36
|
+
_Avoid_: multiple Owners as a product feature; inviting Owner
|
|
37
|
+
|
|
38
|
+
**Invitation**:
|
|
39
|
+
The accept token that attaches a User to an invited Membership. Not a person and not assignable.
|
|
40
|
+
_Avoid_: Waitlist code, pending Member
|
|
41
|
+
|
|
42
|
+
**Closed**:
|
|
43
|
+
A User or Organization in the restorable lockout state.
|
|
44
|
+
_Avoid_: soft-delete (that is Membership leave), deactivated, suspended, banned
|
|
45
|
+
|
|
46
|
+
**Restore**:
|
|
47
|
+
Returning a Closed User or Organization to usable state before Purge. Admin panel only.
|
|
48
|
+
_Avoid_: undelete, reactivate
|
|
49
|
+
|
|
50
|
+
**Purge**:
|
|
51
|
+
Permanent removal of a Closed User or Organization after the grace window.
|
|
52
|
+
_Avoid_: hard-delete as the User-facing noun
|
|
53
|
+
|
|
54
|
+
### Identity and access
|
|
55
|
+
|
|
56
|
+
**Actor**:
|
|
57
|
+
Who a server call is made on behalf of: User, Organization, or Admin. Organization scope requires a live Membership and an Organization that is not Closed. Invited Members are not Actors.
|
|
58
|
+
_Avoid_: Session, Principal, Request, TeamActor
|
|
59
|
+
|
|
60
|
+
**Grant**:
|
|
61
|
+
A permission tuple: resource, level (`user` | `organization`), role, Action, Access.
|
|
62
|
+
_Avoid_: Permission, Policy, ACL
|
|
63
|
+
|
|
64
|
+
**Action**:
|
|
65
|
+
Canonical Grant verbs: `read`, `write`, `delete`, `publish`.
|
|
66
|
+
_Avoid_: update, create, patch, remove
|
|
67
|
+
|
|
68
|
+
**Access**:
|
|
69
|
+
How wide a Grant is: `all`, `own`, `org`, `none`.
|
|
70
|
+
_Avoid_: Scope, Role
|
|
71
|
+
|
|
72
|
+
**Role**:
|
|
73
|
+
A named key at User or Organization scope. Defaults: User `user`/`admin`; Organization `member`/`admin`/`owner`.
|
|
74
|
+
_Avoid_: Grant, Access, permission
|
|
75
|
+
|
|
76
|
+
### Product surfaces
|
|
77
|
+
|
|
78
|
+
**Admin panel**:
|
|
79
|
+
The User-role admin UI at `/admin`. Organization Role `admin` does not see it.
|
|
80
|
+
_Avoid_: org Role admin, Billing Portal
|
|
81
|
+
# m5k:files:start
|
|
82
|
+
|
|
83
|
+
**File**:
|
|
84
|
+
An inventoried object in storage: a row plus the bytes. Personal Files stamp UserId; org-scoped Files stamp MemberId.
|
|
85
|
+
_Avoid_: Upload, Asset, Attachment, Blob
|
|
86
|
+
# m5k:files:end
|
|
87
|
+
# m5k:billing:start
|
|
88
|
+
|
|
89
|
+
**Plan**:
|
|
90
|
+
Named commercial offering in app code. Not a Stripe Product.
|
|
91
|
+
_Avoid_: Product, Tier, Subscription
|
|
92
|
+
|
|
93
|
+
**Subscription**:
|
|
94
|
+
The billed relationship for an Organization, not a User.
|
|
95
|
+
_Avoid_: Plan, personal User subscription
|
|
96
|
+
|
|
97
|
+
**Checkout**:
|
|
98
|
+
Hosted start of a Subscription when the Organization has none.
|
|
99
|
+
_Avoid_: Billing Portal
|
|
100
|
+
|
|
101
|
+
**Billing Portal**:
|
|
102
|
+
Hosted management of an existing Subscription.
|
|
103
|
+
_Avoid_: Checkout
|
|
104
|
+
|
|
105
|
+
**Seat billing**:
|
|
106
|
+
When on, billable quantity is the count of Memberships. Off (default): quantity is one.
|
|
107
|
+
_Avoid_: Seat as a synonym for Membership
|
|
108
|
+
# m5k:billing:end
|
|
109
|
+
# m5k:ai:start
|
|
110
|
+
|
|
111
|
+
**Conversation**:
|
|
112
|
+
Ordered UI transcript of AI turns.
|
|
113
|
+
_Avoid_: Chat, Thread (that is the store)
|
|
114
|
+
|
|
115
|
+
**Thread**:
|
|
116
|
+
The persisted AI memory thread behind a Conversation.
|
|
117
|
+
_Avoid_: using Thread for the UI surface
|
|
118
|
+
|
|
119
|
+
**Agent**:
|
|
120
|
+
A named AI agent the app registers. A Conversation selects which Agent answers.
|
|
121
|
+
_Avoid_: Assistant, Bot, MCP client
|
|
122
|
+
# m5k:ai:end
|
|
123
|
+
# m5k:mcp:start
|
|
124
|
+
|
|
125
|
+
**MCP client**:
|
|
126
|
+
A remote Model Context Protocol client that calls the app on behalf of a User.
|
|
127
|
+
_Avoid_: Agent, Connection
|
|
128
|
+
|
|
129
|
+
**MCP call**:
|
|
130
|
+
A tRPC procedure opted into the MCP catalog. Grant checks always run.
|
|
131
|
+
_Avoid_: a second tool catalog, Workflow job
|
|
132
|
+
|
|
133
|
+
**MCP allowlist**:
|
|
134
|
+
The Organizations a given OAuth client may pass as organizationId for a User.
|
|
135
|
+
_Avoid_: treating allowlist as permission to write
|
|
136
|
+
# m5k:mcp:end
|
|
137
|
+
# m5k:notifications:start
|
|
138
|
+
|
|
139
|
+
**Notification**:
|
|
140
|
+
A persisted inbox instance owned by a Member, classified by a Notification kind.
|
|
141
|
+
_Avoid_: UserId as the ownership key
|
|
142
|
+
|
|
143
|
+
**Notification kind**:
|
|
144
|
+
A developer-declared class of Notifications. Mute applies to the kind.
|
|
145
|
+
_Avoid_: Notification (that is the instance)
|
|
146
|
+
|
|
147
|
+
**Channel**:
|
|
148
|
+
A delivery means for a Notification: in-app, web push, mobile push, or email.
|
|
149
|
+
_Avoid_: Server event, Device
|
|
150
|
+
# m5k:notifications:end
|
|
151
|
+
# m5k:workflows:start
|
|
152
|
+
|
|
153
|
+
**Workflow**:
|
|
154
|
+
A persisted background run. Org-scoped runs stamp MemberId.
|
|
155
|
+
_Avoid_: Job, Queue, Task, Recurrence
|
|
156
|
+
# m5k:workflows:end
|
|
157
|
+
# m5k:tags:start
|
|
158
|
+
|
|
159
|
+
**Tag**:
|
|
160
|
+
A polymorphic label. Ownership is UserId, MemberId, or organizationId.
|
|
161
|
+
_Avoid_: Label, Category
|
|
162
|
+
# m5k:tags:end
|
|
163
|
+
# m5k:connect:start
|
|
164
|
+
|
|
165
|
+
**Connection**:
|
|
166
|
+
A linked third-party API account. Personal; keyed by UserId. The module id is `connect`.
|
|
167
|
+
_Avoid_: Connect as the product noun; login OAuth
|
|
168
|
+
# m5k:connect:end
|
|
169
|
+
# m5k:webhook:start
|
|
170
|
+
|
|
171
|
+
**Inbound callback**:
|
|
172
|
+
A one-shot inbound callback with an awaitable payload. Unattributed. The module id is `webhook`.
|
|
173
|
+
_Avoid_: Webhook (when you mean this vs Stripe Subscription sync); Connection
|
|
174
|
+
# m5k:webhook:end
|
|
175
|
+
# m5k:recurrence:start
|
|
176
|
+
|
|
177
|
+
**Recurrence**:
|
|
178
|
+
A repeating calendar pattern stored as a row. Not a Workflow.
|
|
179
|
+
_Avoid_: Workflow, cron
|
|
180
|
+
# m5k:recurrence:end
|
|
181
|
+
# m5k:server:end
|
|
@@ -39,6 +39,8 @@ Database tables are registered by hand in `apps/server/src/schema.ts`; after cha
|
|
|
39
39
|
|
|
40
40
|
- Email: `admin@{{APP_SLUG}}.local`
|
|
41
41
|
- Password: `password1234`
|
|
42
|
+
|
|
43
|
+
After sign-in, the index route (`/`) is the Posts list.
|
|
42
44
|
// m5k:server:end
|
|
43
45
|
|
|
44
46
|
## Typical Commands
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
# runtime artifacts
|
|
2
|
-
server/local.db*
|
|
3
|
-
server/.e2e/
|
|
4
|
-
server/.emails/
|
|
5
|
-
server/uploads/
|
|
6
|
-
|
|
7
|
-
# e2e output
|
|
8
|
-
e2e/test-results/
|
|
9
|
-
e2e/playwright-report/
|
|
10
|
-
|
|
11
|
-
# expo
|
|
12
|
-
expo/.expo/
|
|
13
|
-
expo/dist/
|
|
1
|
+
# runtime artifacts
|
|
2
|
+
server/local.db*
|
|
3
|
+
server/.e2e/
|
|
4
|
+
server/.emails/
|
|
5
|
+
server/uploads/
|
|
6
|
+
|
|
7
|
+
# e2e output
|
|
8
|
+
e2e/test-results/
|
|
9
|
+
e2e/playwright-report/
|
|
10
|
+
|
|
11
|
+
# expo
|
|
12
|
+
expo/.expo/
|
|
13
|
+
expo/dist/
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{PACKAGE_SCOPE}}/e2e",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"build": "tsc --noEmit",
|
|
7
|
-
"lint": "biome check .",
|
|
8
|
-
"lint:fix": "biome check . --write",
|
|
9
|
-
"check-types": "tsc --noEmit",
|
|
10
|
-
"test:e2e": "playwright test",
|
|
11
|
-
"test:e2e:expo": "playwright test --config playwright.expo.config.ts --headed"
|
|
12
|
-
},
|
|
13
|
-
"devDependencies": {
|
|
14
|
-
"@m5kdev/backend": "catalog:m5kdev",
|
|
15
|
-
"@m5kdev/commons": "catalog:m5kdev",
|
|
16
|
-
"@m5kdev/config": "catalog:m5kdev",
|
|
17
|
-
"@playwright/test": "catalog:m5kdev",
|
|
18
|
-
"{{PACKAGE_SCOPE}}/shared": "workspace:*",
|
|
19
|
-
"@trpc/client": "catalog:m5kdev",
|
|
20
|
-
"@types/node": "catalog:m5kdev",
|
|
21
|
-
"typescript": "catalog:m5kdev"
|
|
22
|
-
}
|
|
23
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PACKAGE_SCOPE}}/e2e",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc --noEmit",
|
|
7
|
+
"lint": "biome check .",
|
|
8
|
+
"lint:fix": "biome check . --write",
|
|
9
|
+
"check-types": "tsc --noEmit",
|
|
10
|
+
"test:e2e": "playwright test",
|
|
11
|
+
"test:e2e:expo": "playwright test --config playwright.expo.config.ts --headed"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@m5kdev/backend": "catalog:m5kdev",
|
|
15
|
+
"@m5kdev/commons": "catalog:m5kdev",
|
|
16
|
+
"@m5kdev/config": "catalog:m5kdev",
|
|
17
|
+
"@playwright/test": "catalog:m5kdev",
|
|
18
|
+
"{{PACKAGE_SCOPE}}/shared": "workspace:*",
|
|
19
|
+
"@trpc/client": "catalog:m5kdev",
|
|
20
|
+
"@types/node": "catalog:m5kdev",
|
|
21
|
+
"typescript": "catalog:m5kdev"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "{{PACKAGE_SCOPE}}/email",
|
|
3
|
-
"version": "0.0.0",
|
|
4
|
-
"private": true,
|
|
5
|
-
"scripts": {
|
|
6
|
-
"build": "tsc --noEmit",
|
|
7
|
-
"lint": "biome check .",
|
|
8
|
-
"lint:fix": "biome check . --write",
|
|
9
|
-
"check-types": "tsc --noEmit",
|
|
10
|
-
"email:dev": "react-email dev --dir src/emails"
|
|
11
|
-
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"@m5kdev/commons": "catalog:m5kdev",
|
|
14
|
-
"@m5kdev/email": "catalog:m5kdev",
|
|
15
|
-
"@react-email/components": "catalog:m5kdev",
|
|
16
|
-
"react": "catalog:m5kdev",
|
|
17
|
-
"react-dom": "catalog:m5kdev"
|
|
18
|
-
},
|
|
19
|
-
"devDependencies": {
|
|
20
|
-
"@m5kdev/backend": "catalog:m5kdev",
|
|
21
|
-
"@m5kdev/config": "catalog:m5kdev",
|
|
22
|
-
"@types/node": "catalog:m5kdev",
|
|
23
|
-
"@types/react": "catalog:m5kdev",
|
|
24
|
-
"@types/react-dom": "catalog:m5kdev",
|
|
25
|
-
"react-email": "catalog:m5kdev",
|
|
26
|
-
"tslib": "catalog:m5kdev",
|
|
27
|
-
"typescript": "catalog:m5kdev"
|
|
28
|
-
},
|
|
29
|
-
"main": "./src/index.ts",
|
|
30
|
-
"exports": {
|
|
31
|
-
".": "./src/index.ts",
|
|
32
|
-
"./resources": "./src/resources.ts"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "{{PACKAGE_SCOPE}}/email",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "tsc --noEmit",
|
|
7
|
+
"lint": "biome check .",
|
|
8
|
+
"lint:fix": "biome check . --write",
|
|
9
|
+
"check-types": "tsc --noEmit",
|
|
10
|
+
"email:dev": "react-email dev --dir src/emails"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@m5kdev/commons": "catalog:m5kdev",
|
|
14
|
+
"@m5kdev/email": "catalog:m5kdev",
|
|
15
|
+
"@react-email/components": "catalog:m5kdev",
|
|
16
|
+
"react": "catalog:m5kdev",
|
|
17
|
+
"react-dom": "catalog:m5kdev"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@m5kdev/backend": "catalog:m5kdev",
|
|
21
|
+
"@m5kdev/config": "catalog:m5kdev",
|
|
22
|
+
"@types/node": "catalog:m5kdev",
|
|
23
|
+
"@types/react": "catalog:m5kdev",
|
|
24
|
+
"@types/react-dom": "catalog:m5kdev",
|
|
25
|
+
"react-email": "catalog:m5kdev",
|
|
26
|
+
"tslib": "catalog:m5kdev",
|
|
27
|
+
"typescript": "catalog:m5kdev"
|
|
28
|
+
},
|
|
29
|
+
"main": "./src/index.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": "./src/index.ts",
|
|
32
|
+
"./resources": "./src/resources.ts"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
|
2
|
-
|
|
3
|
-
# Dependencies
|
|
4
|
-
node_modules/
|
|
5
|
-
|
|
6
|
-
# Expo
|
|
7
|
-
.expo/
|
|
8
|
-
dist/
|
|
9
|
-
web-build/
|
|
10
|
-
expo-env.d.ts
|
|
11
|
-
|
|
12
|
-
# Uniwind (generated on first `expo start`)
|
|
13
|
-
uniwind-types.d.ts
|
|
14
|
-
|
|
15
|
-
# Native
|
|
16
|
-
.kotlin/
|
|
17
|
-
*.orig.*
|
|
18
|
-
*.jks
|
|
19
|
-
*.p8
|
|
20
|
-
*.p12
|
|
21
|
-
*.key
|
|
22
|
-
*.mobileprovision
|
|
23
|
-
|
|
24
|
-
# Metro
|
|
25
|
-
.metro-health-check*
|
|
26
|
-
|
|
27
|
-
# Debug
|
|
28
|
-
npm-debug.*
|
|
29
|
-
yarn-debug.*
|
|
30
|
-
yarn-error.*
|
|
31
|
-
|
|
32
|
-
# macOS
|
|
33
|
-
.DS_Store
|
|
34
|
-
*.pem
|
|
35
|
-
|
|
36
|
-
# Local env files
|
|
37
|
-
.env*.local
|
|
38
|
-
|
|
39
|
-
# TypeScript
|
|
40
|
-
*.tsbuildinfo
|
|
41
|
-
|
|
42
|
-
# Generated native folders
|
|
43
|
-
/ios
|
|
44
|
-
/android
|
|
1
|
+
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
|
2
|
+
|
|
3
|
+
# Dependencies
|
|
4
|
+
node_modules/
|
|
5
|
+
|
|
6
|
+
# Expo
|
|
7
|
+
.expo/
|
|
8
|
+
dist/
|
|
9
|
+
web-build/
|
|
10
|
+
expo-env.d.ts
|
|
11
|
+
|
|
12
|
+
# Uniwind (generated on first `expo start`)
|
|
13
|
+
uniwind-types.d.ts
|
|
14
|
+
|
|
15
|
+
# Native
|
|
16
|
+
.kotlin/
|
|
17
|
+
*.orig.*
|
|
18
|
+
*.jks
|
|
19
|
+
*.p8
|
|
20
|
+
*.p12
|
|
21
|
+
*.key
|
|
22
|
+
*.mobileprovision
|
|
23
|
+
|
|
24
|
+
# Metro
|
|
25
|
+
.metro-health-check*
|
|
26
|
+
|
|
27
|
+
# Debug
|
|
28
|
+
npm-debug.*
|
|
29
|
+
yarn-debug.*
|
|
30
|
+
yarn-error.*
|
|
31
|
+
|
|
32
|
+
# macOS
|
|
33
|
+
.DS_Store
|
|
34
|
+
*.pem
|
|
35
|
+
|
|
36
|
+
# Local env files
|
|
37
|
+
.env*.local
|
|
38
|
+
|
|
39
|
+
# TypeScript
|
|
40
|
+
*.tsbuildinfo
|
|
41
|
+
|
|
42
|
+
# Generated native folders
|
|
43
|
+
/ios
|
|
44
|
+
/android
|