create-stitchkit 0.1.0 → 0.2.0

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +11 -4
  3. package/dist/cli.js +99 -12
  4. package/examples/repository/_env.append +3 -0
  5. package/examples/repository/_env.example.append +4 -0
  6. package/examples/repository/e2e/repository.spec.ts +19 -0
  7. package/{template → examples/repository}/packages/backend/src/domain/repository/github-cache.ts +2 -1
  8. package/examples/repository/packages/backend/src/surface.ts +14 -0
  9. package/examples/repository/packages/config/src/features.ts +7 -0
  10. package/examples/repository/packages/db/schema.prisma +31 -0
  11. package/examples/repository/packages/frontend/src/app/[locale]/page.tsx +45 -0
  12. package/examples/repository/packages/frontend/src/app/[locale]/starter-page.tsx +151 -0
  13. package/examples/repository/packages/frontend/src/providers/index.tsx +18 -0
  14. package/examples/repository/packages/shared/src/index.ts +3 -0
  15. package/examples/repository/scripts/runtime-smoke.ts +91 -0
  16. package/package.json +10 -1
  17. package/template/AGENTS.md +50 -0
  18. package/template/README.md +18 -5
  19. package/template/_env +1 -3
  20. package/template/_env.example +1 -4
  21. package/template/app.config.json +9 -0
  22. package/template/bun.lock +13 -141
  23. package/template/docs/ADDING_A_FEATURE.md +101 -0
  24. package/template/docs/LAN_HTTPS.md +28 -0
  25. package/template/e2e/starter.spec.ts +17 -22
  26. package/template/ecosystem.config.cjs +3 -2
  27. package/template/ecosystem.dev.config.cjs +19 -4
  28. package/template/package.json +4 -2
  29. package/template/packages/backend/package.json +1 -1
  30. package/template/packages/backend/src/cli.ts +2 -1
  31. package/template/packages/backend/src/index.ts +22 -6
  32. package/template/packages/backend/src/surface-manifest.test.ts +62 -0
  33. package/template/packages/backend/src/surface-manifest.ts +117 -0
  34. package/template/packages/backend/src/surface.ts +3 -9
  35. package/template/packages/backend/src/transport/lan-onboarding.ts +29 -0
  36. package/template/packages/config/package.json +2 -1
  37. package/template/packages/config/src/features.ts +1 -0
  38. package/template/packages/config/src/identity.ts +18 -0
  39. package/template/packages/config/src/server.ts +13 -3
  40. package/template/packages/db/schema.prisma +0 -23
  41. package/template/packages/frontend/messages/en.json +0 -1
  42. package/template/packages/frontend/messages/ru.json +0 -1
  43. package/template/packages/frontend/package.json +1 -0
  44. package/template/packages/frontend/src/app/[locale]/page.tsx +8 -19
  45. package/template/packages/frontend/src/app/[locale]/starter-page.tsx +3 -4
  46. package/template/packages/frontend/src/app/[locale]/ui/_catalogue/landing-showcase.tsx +3 -2
  47. package/template/packages/frontend/src/lib/seo/pages.ts +4 -5
  48. package/template/packages/frontend/src/providers/index.tsx +1 -4
  49. package/template/packages/frontend/src/theme/config.ts +2 -1
  50. package/template/packages/shared/src/index.ts +1 -3
  51. package/template/scripts/dev-lan.test.ts +58 -0
  52. package/template/scripts/dev-lan.ts +176 -0
  53. package/template/scripts/dev.ts +37 -7
  54. package/template/scripts/runtime-smoke.ts +13 -57
  55. package/template/scripts/surface-conformance.ts +105 -0
  56. /package/{template → examples/repository}/packages/backend/src/domain/errors.ts +0 -0
  57. /package/{template → examples/repository}/packages/backend/src/domain/repository/github-cache.test.ts +0 -0
  58. /package/{template → examples/repository}/packages/backend/src/transport/repository-service.ts +0 -0
  59. /package/{template → examples/repository}/packages/db/migrations/20260808000000_init/migration.sql +0 -0
  60. /package/{template → examples/repository}/packages/db/migrations/20260808170000_repository_visibility/migration.sql +0 -0
  61. /package/{template → examples/repository}/packages/frontend/src/components/repository-summary.tsx +0 -0
  62. /package/{template → examples/repository}/packages/frontend/src/lib/api/client.ts +0 -0
  63. /package/{template → examples/repository}/packages/frontend/src/lib/api/queries.ts +0 -0
  64. /package/{template → examples/repository}/packages/frontend/src/lib/realtime/repository.ts +0 -0
  65. /package/{template → examples/repository}/packages/frontend/src/providers/realtime.tsx +0 -0
  66. /package/{template → examples/repository}/packages/shared/src/contracts/repository.ts +0 -0
  67. /package/{template → examples/repository}/packages/shared/src/events/repository.ts +0 -0
  68. /package/{template → examples/repository}/packages/shared/src/schemas/repository.test.ts +0 -0
  69. /package/{template → examples/repository}/packages/shared/src/schemas/repository.ts +0 -0
@@ -0,0 +1,91 @@
1
+ import { RepositorySnapshotSchema } from '@app/shared';
2
+ import { io } from 'socket.io-client';
3
+ import { z } from 'zod';
4
+ import { defineSurfaceProbe, runSurfaceConformance } from './surface-conformance';
5
+ import { toolingEnv } from './tooling-env';
6
+
7
+ const apiOrigin = toolingEnv.NEXT_PUBLIC_API_URL;
8
+
9
+ async function json(path: string, init?: RequestInit): Promise<unknown> {
10
+ const response = await fetch(`${apiOrigin}${path}`, init);
11
+ if (!response.ok)
12
+ throw new Error(`${init?.method ?? 'GET'} ${path} returned ${response.status}`);
13
+ return response.json();
14
+ }
15
+
16
+ z.object({ status: z.literal('ok') }).parse(await json('/health'));
17
+
18
+ await runSurfaceConformance({
19
+ apiOrigin,
20
+ probes: [
21
+ defineSurfaceProbe({
22
+ name: 'repository refresh lifecycle',
23
+ input: z.object({ path: z.literal('/api/repository/refresh') }),
24
+ fixture: { path: '/api/repository/refresh' },
25
+ output: RepositorySnapshotSchema,
26
+ run: async ({ path }) => {
27
+ const socket = io(apiOrigin, { transports: ['websocket'] });
28
+ try {
29
+ await new Promise<void>((resolve, reject) => {
30
+ const timeout = setTimeout(
31
+ () => reject(new Error('Socket.IO connection timed out')),
32
+ 5_000,
33
+ );
34
+ const onConnect = () => {
35
+ clearTimeout(timeout);
36
+ socket.off('connect_error', onConnectError);
37
+ resolve();
38
+ };
39
+ const onConnectError = (error: Error) => {
40
+ clearTimeout(timeout);
41
+ socket.off('connect', onConnect);
42
+ reject(error);
43
+ };
44
+ socket.once('connect', onConnect);
45
+ socket.once('connect_error', onConnectError);
46
+ });
47
+ const refreshedEvent = new Promise<string>((resolve, reject) => {
48
+ const timeout = setTimeout(
49
+ () => reject(new Error('Socket.IO repository refresh event timed out')),
50
+ 5_000,
51
+ );
52
+ socket.once('repository:refreshed', (snapshot) => {
53
+ clearTimeout(timeout);
54
+ resolve(snapshot.fullName);
55
+ });
56
+ });
57
+ const refreshed = RepositorySnapshotSchema.parse(
58
+ await json(path, { method: 'POST' }),
59
+ );
60
+ if ((await refreshedEvent) !== refreshed.fullName) {
61
+ throw new Error('Socket.IO repository identity differs');
62
+ }
63
+ const cached = RepositorySnapshotSchema.parse(await json('/api/repository/'));
64
+ if (cached.fullName !== refreshed.fullName) {
65
+ throw new Error('Repository cache read differs');
66
+ }
67
+ return refreshed;
68
+ } finally {
69
+ socket.close();
70
+ }
71
+ },
72
+ }),
73
+ ],
74
+ });
75
+
76
+ {
77
+ const corsResponse = await fetch(`${apiOrigin}/api/repository/`, {
78
+ headers: { Origin: 'http://localhost:58302' },
79
+ });
80
+ if (corsResponse.headers.get('access-control-allow-origin') !== '*') {
81
+ throw new Error('Public API does not allow a forwarded browser origin');
82
+ }
83
+ const openApi = z
84
+ .object({ paths: z.record(z.string(), z.unknown()) })
85
+ .parse(await json('/openapi.json'));
86
+ if (!Object.keys(openApi.paths).some((path) => path.startsWith('/api/repository'))) {
87
+ throw new Error('OpenAPI repository path is missing');
88
+ }
89
+ }
90
+
91
+ console.log('Runtime HTTP, OpenAPI, Socket.IO and MCP smoke passed');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-stitchkit",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Create a production-shaped Stitchkit application",
5
5
  "license": "MIT",
6
6
  "author": "Max Listov <maxlistov@gmail.com>",
@@ -16,6 +16,7 @@
16
16
  "files": [
17
17
  "dist",
18
18
  "template/**/*",
19
+ "examples/**/*",
19
20
  "!template/**/.env",
20
21
  "!template/**/node_modules/**",
21
22
  "!template/**/.next/**",
@@ -26,6 +27,11 @@
26
27
  "!template/**/src/generated/**",
27
28
  "!template/**/*.log",
28
29
  "!template/**/*.tsbuildinfo",
30
+ "!examples/**/node_modules/**",
31
+ "!examples/**/.next/**",
32
+ "!examples/**/dist/**",
33
+ "!examples/**/*.log",
34
+ "!examples/**/*.tsbuildinfo",
29
35
  "README.md",
30
36
  "CHANGELOG.md",
31
37
  "LICENSE"
@@ -39,6 +45,9 @@
39
45
  "build": "rm -rf dist && bun build src/cli.ts --outdir dist --target bun --packages external && chmod +x dist/cli.js",
40
46
  "prepublishOnly": "bun run check && bun run test && bun run build"
41
47
  },
48
+ "dependencies": {
49
+ "zod": "^4.4.3"
50
+ },
42
51
  "devDependencies": {
43
52
  "@types/bun": "^1.3.14",
44
53
  "typescript": "^7.0.2"
@@ -0,0 +1,50 @@
1
+ # Application agent guide
2
+
3
+ This repository is a generated Stitchkit application. It is not the Stitchkit
4
+ framework source repository.
5
+
6
+ ## Architecture
7
+
8
+ - `packages/shared` owns named Zod schemas, inferred DTO types, HTTP contracts
9
+ and realtime event definitions. It has no database, server or browser imports.
10
+ - `packages/db` owns Prisma schema, migrations and the generated client.
11
+ - `packages/backend` implements contracts, composes the registered surface and
12
+ owns application policy. Routes remain thin transport boundaries.
13
+ - `packages/frontend` owns Next.js pages, typed clients, query/mutation hooks and
14
+ cache reactions.
15
+ - `packages/config/src/server.ts` is the only server environment boundary.
16
+ `app.config.json` is the only application identity boundary.
17
+
18
+ Dependencies point inward: frontend/backend → shared; backend → db/config.
19
+ Shared never imports an application runtime package.
20
+
21
+ ## Required patterns
22
+
23
+ - Define each runtime DTO as a named Zod schema in `packages/shared/src/schemas`.
24
+ - Reference schemas from a separate contract module; never inline `z.object()`
25
+ inside `defineContract`.
26
+ - Implement one service method per contract operation and register the service
27
+ once in `packages/backend/src/surface.ts`.
28
+ - Use Stitchkit's typed browser client and react-query-kit hooks; do not rebuild
29
+ endpoint URLs, query keys or error envelopes by hand.
30
+ - Use Socket.IO through the shared realtime contract and Stitchkit wrappers.
31
+ Authentication, authorization and room membership remain application policy.
32
+ - Extend runtime smoke with an explicit typed probe for operations whose handler
33
+ behavior matters. Generic OpenAPI/MCP discovery checks are already derived.
34
+
35
+ Do not duplicate DTOs, copy Stitchkit internals, add raw routes for operations a
36
+ contract can express, or create compatibility aliases. Fix framework gaps in
37
+ Stitchkit rather than copying its implementation into this application.
38
+
39
+ ## Workflow
40
+
41
+ Follow [`docs/ADDING_A_FEATURE.md`](docs/ADDING_A_FEATURE.md) for the complete
42
+ vertical path. Before handing work off, run:
43
+
44
+ ```bash
45
+ bun run check
46
+ bun run test
47
+ bun run build
48
+ bun run runtime:smoke
49
+ ```
50
+
@@ -2,6 +2,10 @@
2
2
 
3
3
  Production-shaped application generated by `create-stitchkit`.
4
4
 
5
+ Application identity lives in [`app.config.json`](app.config.json). Change the
6
+ slug, display name, version and localized description there; package names,
7
+ process names, MCP/OpenAPI identity, UI copy and SEO derive from it.
8
+
5
9
  ## Start
6
10
 
7
11
  Point `DATABASE_URL` in `.env` at an existing PostgreSQL database, then run:
@@ -19,11 +23,16 @@ checked-in migrations and launches:
19
23
  - OpenAPI: <http://localhost:3211/openapi.json>
20
24
  - MCP: <http://localhost:3211/mcp>
21
25
 
22
- The home page is a compact Stitchkit Starter reference surface. One configured
23
- GitHub repository exercises the PostgreSQL cache → contract → typed client →
26
+ The home page is a compact, domain-free Stitchkit Starter reference surface.
27
+ Add application features as vertical schema → contract → service → client slices.
28
+ The exact workflow is in [`docs/ADDING_A_FEATURE.md`](docs/ADDING_A_FEATURE.md),
29
+ and root [`AGENTS.md`](AGENTS.md) gives coding agents the application boundaries.
30
+
31
+ When generated with `--example repository`, one configured GitHub repository
32
+ exercises the PostgreSQL cache → contract → typed client →
24
33
  mutation invalidation → realtime path without adding a demo product domain.
25
34
  Configure it with `GITHUB_REPOSITORY`; `GITHUB_TOKEN` is optional.
26
- Repository visibility demonstrates the canonical Prisma enum → shared Zod schema
35
+ Repository visibility then demonstrates the canonical Prisma enum → shared Zod schema
27
36
  → HTTP/UI path without duplicating its allowed values.
28
37
 
29
38
  The `/en/ui` catalogue is isolated under
@@ -47,6 +56,12 @@ bun run test
47
56
  bun run build
48
57
  ```
49
58
 
59
+ ## Physical-device HTTPS
60
+
61
+ `bun run dev:lan` is an explicit trusted-LAN mode powered by mkcert. It leaves
62
+ normal development and production unchanged. Setup and device trust steps are
63
+ in [`docs/LAN_HTTPS.md`](docs/LAN_HTTPS.md).
64
+
50
65
  ## Production
51
66
 
52
67
  Provide a production `.env`, then:
@@ -65,5 +80,3 @@ application owns its schema and migrations; the environment owns the database
65
80
  process and supplies its connection through `DATABASE_URL`.
66
81
 
67
82
  `bun run dev` and `bun run pm2:dev` use the same direct PM2 development path.
68
- Rename the neutral `stitchkit-starter` package and process names when adopting
69
- the template for a product.
package/template/_env CHANGED
@@ -6,6 +6,4 @@ NEXT_PUBLIC_API_URL=http://127.0.0.1:3211
6
6
  INTERNAL_API_URL=http://127.0.0.1:3211
7
7
  NEXT_PUBLIC_WEB_URL=http://127.0.0.1:3210
8
8
  LOG_FORMAT=pretty
9
- GITHUB_REPOSITORY=max-listov/stitchkit
10
- GITHUB_CACHE_TTL_SECONDS=900
11
- # GITHUB_TOKEN=github_pat_...
9
+ CORS_ORIGIN=*
@@ -6,7 +6,4 @@ NEXT_PUBLIC_API_URL=http://127.0.0.1:3211
6
6
  INTERNAL_API_URL=http://127.0.0.1:3211
7
7
  NEXT_PUBLIC_WEB_URL=http://127.0.0.1:3210
8
8
  LOG_FORMAT=pretty
9
- GITHUB_REPOSITORY=max-listov/stitchkit
10
- GITHUB_CACHE_TTL_SECONDS=900
11
- # Optional. Authenticated conditional requests have a higher GitHub rate limit.
12
- # GITHUB_TOKEN=github_pat_...
9
+ CORS_ORIGIN=*
@@ -0,0 +1,9 @@
1
+ {
2
+ "slug": "stitchkit-starter",
3
+ "name": "Stitchkit Starter",
4
+ "version": "0.1.0",
5
+ "description": {
6
+ "en": "Stitchkit Starter is a production application built with Stitchkit.",
7
+ "ru": "Stitchkit Starter — production-приложение на Stitchkit."
8
+ }
9
+ }