create-tilcayo-app 0.0.1 → 0.0.4
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/README.md +294 -49
- package/dist/bin.js +13 -4
- package/dist/fullstack.d.ts +2 -0
- package/dist/fullstack.js +107 -0
- package/dist/index.js +38 -10
- package/dist/installTenancy.d.ts +2 -0
- package/dist/installTenancy.js +86 -0
- package/dist/options.d.ts +4 -2
- package/dist/options.js +58 -14
- package/dist/react-auth.json +83 -0
- package/dist/react-starter.json +75 -0
- package/dist/templates.js +113 -74
- package/package.json +27 -26
package/README.md
CHANGED
|
@@ -1,49 +1,294 @@
|
|
|
1
|
-
# create-tilcayo-app
|
|
2
|
-
|
|
3
|
-
Create a Tilcayo API with an interactive setup wizard:
|
|
4
|
-
|
|
5
|
-
```sh
|
|
6
|
-
create-tilcayo-app
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
npm create tilcayo-app@latest
|
|
13
|
-
pnpm create tilcayo-app
|
|
14
|
-
yarn create tilcayo-app
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
For local development in the Tilcayo repository, build the workspace and run
|
|
18
|
-
`node packages/create-tilcayo-app/dist/bin.js my-api`, or link this package to expose
|
|
19
|
-
`create-tilcayo-app` on PATH.
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
create-tilcayo-app my-api --type api --auth --package-manager npm --yes
|
|
23
|
-
create-tilcayo-app tiny-api --type minimal --no-auth --no-install --yes
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
The wizard offers project name, package manager (npm, pnpm, or Yarn), application
|
|
27
|
-
type, and authentication. Use the arrow keys and Enter to choose from colored
|
|
28
|
-
menus; project name remains a text field. Ctrl+C cancels before files are created.
|
|
29
|
-
`api` includes MongoDB-backed note CRUD; `minimal`
|
|
30
|
-
includes a health endpoint. Enabling authentication adds MongoDB to either type.
|
|
31
|
-
Noninteractive runs use defaults for omitted options. Existing paths are refused.
|
|
32
|
-
|
|
33
|
-
Generated projects contain controllers, routes, models, services, validators,
|
|
34
|
-
separate app and startup modules, TypeScript configuration, `.gitignore`,
|
|
35
|
-
`.env.example`, and a private `.env`. Authentication gets independent random
|
|
36
|
-
secrets in `.env`; the example file leaves secrets blank.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
# create-tilcayo-app
|
|
2
|
+
|
|
3
|
+
Create a Tilcayo API or React client with an interactive setup wizard:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
create-tilcayo-app
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Use Node.js 22.9+. Create your app outside the framework checkout, without a global install:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm create tilcayo-app@latest
|
|
13
|
+
pnpm create tilcayo-app
|
|
14
|
+
yarn create tilcayo-app
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For local development in the Tilcayo repository, build the workspace and run
|
|
18
|
+
`node packages/create-tilcayo-app/dist/bin.js my-api`, or link this package to expose
|
|
19
|
+
`create-tilcayo-app` on PATH.
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
create-tilcayo-app my-api --type api --auth --package-manager npm --yes
|
|
23
|
+
create-tilcayo-app tiny-api --type minimal --no-auth --no-install --yes
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The wizard offers project name, package manager (npm, pnpm, or Yarn), application
|
|
27
|
+
type, and authentication. Use the arrow keys and Enter to choose from colored
|
|
28
|
+
menus; project name remains a text field. Ctrl+C cancels before files are created.
|
|
29
|
+
`api` includes MongoDB-backed note CRUD; `minimal`
|
|
30
|
+
includes a health endpoint. Enabling authentication adds MongoDB to either type.
|
|
31
|
+
Noninteractive runs use defaults for omitted options. Existing paths are refused.
|
|
32
|
+
|
|
33
|
+
Generated projects contain controllers, routes, models, services, validators,
|
|
34
|
+
separate app and startup modules, TypeScript configuration, `.gitignore`,
|
|
35
|
+
`.env.example`, and a private `.env`. Authentication gets independent random
|
|
36
|
+
secrets in `.env`; the example file leaves secrets blank.
|
|
37
|
+
|
|
38
|
+
## Start your API
|
|
39
|
+
|
|
40
|
+
For a MongoDB API with authentication:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npm create tilcayo-app@latest my-api -- --type api --auth --yes
|
|
44
|
+
cd my-api
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
In your new app's `.env`, configure the database:
|
|
48
|
+
|
|
49
|
+
```dotenv
|
|
50
|
+
PORT=9149
|
|
51
|
+
MONGODB_URI=mongodb://127.0.0.1:27017/my_api
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Keep the generated authentication secret entries. Start MongoDB locally or replace
|
|
55
|
+
the URI with your Atlas connection string. Then run:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
npm run dev
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Open `http://localhost:9149/health`:
|
|
62
|
+
|
|
63
|
+
```json
|
|
64
|
+
{"success":true,"message":"Success","data":{"status":"ok"}}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The `api` and `minimal` starters are backend APIs. On PowerShell, use
|
|
68
|
+
`npm.cmd` / `npx.cmd` if script execution is blocked.
|
|
69
|
+
|
|
70
|
+
## Options
|
|
71
|
+
|
|
72
|
+
With `npm create`, place creator flags after `--`.
|
|
73
|
+
|
|
74
|
+
| Option | Purpose |
|
|
75
|
+
| --- | --- |
|
|
76
|
+
| `[project-name]` | New directory and package name |
|
|
77
|
+
| `--type api\|minimal\|react` | MongoDB API, minimal API, or React client |
|
|
78
|
+
| `--auth` / `--no-auth` | Include or omit authentication |
|
|
79
|
+
| `--package-manager npm\|pnpm\|yarn` | Installation tool |
|
|
80
|
+
| `--no-install` | Write files without installation or compilation |
|
|
81
|
+
| `--yes` | Use defaults for unanswered choices |
|
|
82
|
+
| `--help` | Command help |
|
|
83
|
+
|
|
84
|
+
Defaults are `my-api`, npm, `api`, and no authentication. A minimal app needs no
|
|
85
|
+
database unless authentication is enabled:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
npm create tilcayo-app@latest tiny-api -- --type minimal --no-auth --yes
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Continue building
|
|
92
|
+
|
|
93
|
+
For a React client, select React in the wizard or pass `--type react --yes`.
|
|
94
|
+
Use Node.js 22.12+ or 24+. The client includes the current Tilcayo starter pages,
|
|
95
|
+
responsive hamburger menu, TypeScript, Vite, React Router, and `@tilcayo/styles`.
|
|
96
|
+
Run `npm run dev`, `npm run build`, `npm run preview`, or `npm run lint` in the
|
|
97
|
+
generated client. Add `--auth` for login/register pages, a protected dashboard,
|
|
98
|
+
guest/auth route guards, auth bootstrap and logout navigation. This frontend connects
|
|
99
|
+
to a separate Tilcayo auth API; its Vite proxy targets port 9149.
|
|
100
|
+
|
|
101
|
+
To test from the repository root:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
npm run build
|
|
105
|
+
cd examples
|
|
106
|
+
node ../packages/create-tilcayo-app/dist/bin.js my-react-app --type react --yes
|
|
107
|
+
cd my-react-app
|
|
108
|
+
npm run dev
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The build bundles the `client` source into `dist/react-starter.json`, so the packed
|
|
112
|
+
creator does not need the checkout to generate a React app. Local generation
|
|
113
|
+
links the checkout styles package; published generation uses `@tilcayo/styles`
|
|
114
|
+
from npm. Publish the styles package and this updated creator before testing
|
|
115
|
+
`npm create tilcayo-app@latest my-react-app -- --type react --yes`.
|
|
116
|
+
|
|
117
|
+
For backend starters:
|
|
118
|
+
|
|
119
|
+
| Location | Purpose |
|
|
120
|
+
| --- | --- |
|
|
121
|
+
| `src/app.ts` | Register routes and middleware |
|
|
122
|
+
| `src/index.ts` | Database connection and server lifecycle |
|
|
123
|
+
| `src/config.ts` | Configuration |
|
|
124
|
+
| `src/models/` | Database schemas |
|
|
125
|
+
| `src/controllers/` | Request handlers |
|
|
126
|
+
| `src/validators/` | Zod validation |
|
|
127
|
+
| `src/routes/` | URLs and middleware |
|
|
128
|
+
| `src/services/` | Business logic |
|
|
129
|
+
|
|
130
|
+
In another terminal inside the app, generate a feature:
|
|
131
|
+
|
|
132
|
+
```sh
|
|
133
|
+
npx tilcayo make:resource Product name:string price:number "description:string?"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Add the import and registration to `src/app.ts`, before its default export:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import productsRoutes from "./routes/products.routes.js";
|
|
140
|
+
|
|
141
|
+
app.routes(productsRoutes);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Send `POST http://localhost:9149/products` with `Content-Type: application/json`
|
|
145
|
+
and body `{"name":"Notebook","price":250}` using Postman or Thunder Client.
|
|
146
|
+
Use `GET /products` to list records and `GET`, `PUT`, or `DELETE /products/:id`
|
|
147
|
+
to read, update, or delete one. Replace `:id` with the returned `data._id`.
|
|
148
|
+
|
|
149
|
+
Edit the generated files as your feature grows. Dev rebuilds and restarts on
|
|
150
|
+
source changes. Generators refuse to overwrite files and do not register routes
|
|
151
|
+
automatically.
|
|
152
|
+
|
|
153
|
+
## Authentication
|
|
154
|
+
|
|
155
|
+
For backend starters with `--auth`, these endpoints are registered:
|
|
156
|
+
|
|
157
|
+
| Endpoint | Input |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| `POST /api/auth/register` | JSON `name`, `email`, `password` |
|
|
160
|
+
| `POST /api/auth/login` | JSON `email`, `password` |
|
|
161
|
+
| `POST /api/auth/refresh` | JSON `refreshToken` |
|
|
162
|
+
| `POST /api/auth/logout` | JSON `refreshToken` |
|
|
163
|
+
| `GET /api/auth/me` | Bearer access token |
|
|
164
|
+
|
|
165
|
+
Registration and login return tokens under `data.tokens`. Send the access token
|
|
166
|
+
in `Authorization: Bearer <accessToken>`. Generated notes and other CRUD routes
|
|
167
|
+
remain public until you add auth middleware. See the
|
|
168
|
+
[authentication guide](https://www.npmjs.com/package/@tilcayo/auth).
|
|
169
|
+
|
|
170
|
+
## Build and run
|
|
171
|
+
|
|
172
|
+
| Command | Purpose |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `npm run dev` | Develop with rebuilds and restarts |
|
|
175
|
+
| `npm run build` | Compile into dist |
|
|
176
|
+
| `npm run routes` | List routes from compiled code |
|
|
177
|
+
| `npm start` | Start compiled code with .env |
|
|
178
|
+
|
|
179
|
+
Build before listing changed routes or starting compiled code. Stop dev before
|
|
180
|
+
starting another server on the same port. Deploy the application as a Node.js
|
|
181
|
+
service with its runtime dependencies, environment, and database connection.
|
|
182
|
+
Application feature changes do not require publishing to npm.
|
|
183
|
+
|
|
184
|
+
## Installation and local development
|
|
185
|
+
|
|
186
|
+
Setup installs dependencies and builds unless `--no-install` is supplied. The
|
|
187
|
+
selected package manager must be installed. Running the creator from a built
|
|
188
|
+
Tilcayo source checkout automatically links the local framework packages and
|
|
189
|
+
their shared Zod and Mongoose dependencies. Keep that checkout available while
|
|
190
|
+
developing the generated app. The published creator uses registry versions of
|
|
191
|
+
`@tilcayo/core`, `@tilcayo/auth`, and `@tilcayo/cli` instead.
|
|
192
|
+
Installation failures retain the generated project and report recovery steps.
|
|
193
|
+
MongoDB must be running before starting an API that uses it.
|
|
194
|
+
|
|
195
|
+
If installation fails or was skipped, enter the generated project and run
|
|
196
|
+
`npm install` then `npm run build`; do not regenerate the same directory.
|
|
197
|
+
|
|
198
|
+
Inside the generated project, use `tilcayo dev`, `tilcayo build`, `tilcayo start`,
|
|
199
|
+
and `tilcayo routes:list`. If the CLI is installed only in the project, invoke
|
|
200
|
+
it through `npx tilcayo`, `pnpm exec tilcayo`, or `yarn exec tilcayo`.
|
|
201
|
+
|
|
202
|
+
[Project guide](https://github.com/jomobrain1/tilcayo#readme) ·
|
|
203
|
+
[Generators](https://www.npmjs.com/package/@tilcayo/cli) ·
|
|
204
|
+
[Runtime](https://www.npmjs.com/package/@tilcayo/core)
|
|
205
|
+
|
|
206
|
+
Plain React starters include `@tilcayo/react` and `src/lib/api.ts`, configured with
|
|
207
|
+
public `VITE_API_URL` or `/api`. No API requests run on startup. Local generation
|
|
208
|
+
links both frontend packages; future published generation uses registry versions.
|
|
209
|
+
Publish `@tilcayo/react` as well before releasing this creator. Never put secrets
|
|
210
|
+
in VITE_* variables. See [API client](../react/README.md).
|
|
211
|
+
|
|
212
|
+
### React auth installation (local, unpublished)
|
|
213
|
+
|
|
214
|
+
From `examples`, after building the workspace:
|
|
215
|
+
|
|
216
|
+
```sh
|
|
217
|
+
node ../packages/create-tilcayo-app/dist/bin.js my-auth-app --type react --auth --yes
|
|
218
|
+
cd my-auth-app
|
|
219
|
+
npm run dev
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Open `/register` or `/login`. The generated README explains creating a separate
|
|
223
|
+
auth backend and connecting MongoDB. Frontend files live in `src/app/auth.ts`,
|
|
224
|
+
`src/middleware/auth.tsx`, `src/components/auth-form.tsx`, and `src/pages/`.
|
|
225
|
+
The creator ships the auth overlay in `dist/react-auth.json`. Tokens remain in
|
|
226
|
+
memory. A standalone React app needs a compatible cookie-session backend and matching frontend configuration to restore login on reload; full-stack auth starters configure this automatically. No backend secrets are generated in the frontend.
|
|
227
|
+
Existing generated apps are not overwritten or upgraded by this command.
|
|
228
|
+
## Full-stack presets
|
|
229
|
+
|
|
230
|
+
From a built source checkout:
|
|
231
|
+
|
|
232
|
+
```sh
|
|
233
|
+
node packages/create-tilcayo-app/dist/bin.js bookstore --fullstack --auth --admin --yes
|
|
234
|
+
cd bookstore
|
|
235
|
+
npm run dev
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
`--api` preserves API-only creation. `--fullstack` creates `api/` and `client/`
|
|
239
|
+
workspaces with one dev command, separate dev scripts, both production builds,
|
|
240
|
+
an API proxy and generated Notes CRUD. Set MongoDB in `api/.env`. Auth adds
|
|
241
|
+
login/register/recovery/profile pages and protected routes; admin requires auth
|
|
242
|
+
and adds `/admin` management pages. Provision roles through trusted server code.
|
|
243
|
+
|
|
244
|
+
Run `tilcayo make:resource Book title:string author:ref:Author --fullstack` from
|
|
245
|
+
the generated root and follow the printed route registration steps. Production
|
|
246
|
+
hosts serve `client/dist` with SPA fallback and proxy `/api` to the Node API.
|
|
247
|
+
`npm run start` starts the API only. See the generated README for configuration.
|
|
248
|
+
|
|
249
|
+
The updated frontend packages and creator must be published before these flags
|
|
250
|
+
work through `npm create tilcayo-app@latest`. Verify packed artifacts externally
|
|
251
|
+
with `node tests/fullstack-starter-consumer.mjs` before release.
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
## Tenant workspaces
|
|
255
|
+
|
|
256
|
+
Create a local full-stack auth/admin app with `--tenancy`:
|
|
257
|
+
|
|
258
|
+
```sh
|
|
259
|
+
node packages/create-tilcayo-app/dist/bin.js my-saas --fullstack --auth --admin --tenancy --yes
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
From the generated application, set `TENANCY_SUPERADMIN_EMAIL` and
|
|
263
|
+
`TENANCY_SUPERADMIN_PASSWORD` for the setup process, then run `npm run tenancy:setup`.
|
|
264
|
+
This explicit command provisions only a Super Admin. It does not create a default
|
|
265
|
+
tenant, assign memberships or migrate existing records. Re-running it updates
|
|
266
|
+
the designated account password and revokes its existing sessions. No bootstrap
|
|
267
|
+
password is generated into source files or run automatically on startup.
|
|
268
|
+
|
|
269
|
+
Run the app and use `http://localhost:5173/admin/workspaces` for
|
|
270
|
+
platform management. Create tenants and assign their administrators in Tenant administration. Open `<tenant-slug>.localhost` with the port printed by Vite.
|
|
271
|
+
Users can register on the main site or at a tenant address. Tenant signup creates a normal membership in that tenant. An existing account can join another tenant using its existing password; signup never grants an admin role. There is no default tenant or Main app workspace switcher. The main site supports ordinary users and public registration; tenant administrators manage their own users, roles and product catalog.
|
|
272
|
+
Each hostname has its own HttpOnly session cookie; sign in once per workspace.
|
|
273
|
+
|
|
274
|
+
Super Admin manages tenants and global accounts. Tenant roles belong to Memberships,
|
|
275
|
+
not global Users. A Super Admin must be explicitly added to enter a workspace.
|
|
276
|
+
Tenant administrators manage their own members, custom roles, products, orders and
|
|
277
|
+
Trash. Suspending a membership affects only that workspace; global user suspension
|
|
278
|
+
affects all workspaces. Suspended or archived tenants retain data but reject access.
|
|
279
|
+
|
|
280
|
+
`TENANCY_BASE_DOMAIN` defaults to `localhost`. Production needs wildcard DNS and
|
|
281
|
+
HTTPS for that domain, with a same-origin `/api` proxy preserving Host. Subdomains
|
|
282
|
+
are resolved from Host; forwarded tenant headers are not trusted.
|
|
283
|
+
|
|
284
|
+
Tenant-owned models use `mongoModel(name, schema, { tenantScoped: true })`.
|
|
285
|
+
The generator applies this to existing models and future `make:model` and
|
|
286
|
+
`make:resource` output. Queries, writes, counts, distinct, population and supported
|
|
287
|
+
aggregations use the request's tenant context. Platform requests cannot read these
|
|
288
|
+
records. Native `raw.collection` operations bypass Mongoose and are reserved for
|
|
289
|
+
explicit setup/migration code. Bulk writes, replacement updates and cross-collection
|
|
290
|
+
aggregate stages are rejected unless implemented with their own isolation checks.
|
|
291
|
+
|
|
292
|
+
## Complete command reference
|
|
293
|
+
|
|
294
|
+
See the [main README](../../README.md#create-an-application) for every creator flag, app preset, generator command, environment setting and framework development command. Source checkout builds use the current code; registry creation uses the packages that have been published.
|
package/dist/bin.js
CHANGED
|
@@ -51,6 +51,8 @@ async function main() {
|
|
|
51
51
|
options: [
|
|
52
52
|
{ value: "api", label: color.green("API"), hint: "MongoDB with sample CRUD routes" },
|
|
53
53
|
{ value: "minimal", label: color.magenta("Minimal"), hint: "Health-check endpoint only" },
|
|
54
|
+
{ value: "react", label: color.cyan("React"), hint: "React client with Tilcayo styles and responsive navigation" },
|
|
55
|
+
{ value: "fullstack", label: color.blue("Full stack"), hint: "MongoDB API and React client in one workspace" },
|
|
54
56
|
],
|
|
55
57
|
}));
|
|
56
58
|
if (missing.has("auth"))
|
|
@@ -58,7 +60,7 @@ async function main() {
|
|
|
58
60
|
message: "Include authentication?",
|
|
59
61
|
initialValue: options.auth,
|
|
60
62
|
options: [
|
|
61
|
-
{ value: true, label: color.green("Yes"), hint: "Register, login, refresh and logout; requires MongoDB" },
|
|
63
|
+
{ value: true, label: color.green("Yes"), hint: options.type === "react" ? "Login/register pages, dashboard and route guards; connects to a Tilcayo auth API" : "Register, login, refresh and logout; requires MongoDB" },
|
|
62
64
|
{ value: false, label: color.yellow("No"), hint: "Start without authentication" },
|
|
63
65
|
],
|
|
64
66
|
}));
|
|
@@ -67,15 +69,22 @@ async function main() {
|
|
|
67
69
|
const target = await generateApp(options);
|
|
68
70
|
console.log(`Created ${target}`);
|
|
69
71
|
if (options.install) {
|
|
70
|
-
console.log(`Installing dependencies with ${options.packageManager} and building the
|
|
72
|
+
console.log(`Installing dependencies with ${options.packageManager} and building the application...`);
|
|
71
73
|
await installApp(target, options.packageManager);
|
|
72
74
|
}
|
|
73
75
|
console.log(`\nNext:\n cd ${options.name}`);
|
|
74
76
|
if (!options.install)
|
|
75
77
|
console.log(` ${options.packageManager} install`);
|
|
76
78
|
const cli = options.packageManager === "npm" ? "npx tilcayo" : `${options.packageManager} exec tilcayo`;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
if (options.type === "react" || options.type === "fullstack")
|
|
80
|
+
console.log(` ${options.packageManager} run dev`);
|
|
81
|
+
else
|
|
82
|
+
console.log(` ${cli} dev\n\nWith Tilcayo on PATH, use tilcayo dev directly.`);
|
|
83
|
+
if (options.type === "react" && options.auth)
|
|
84
|
+
console.log("Open /login or /register. Start a Tilcayo auth API on port 9149; see README.md for setup and API URL configuration.");
|
|
85
|
+
else if (options.type === "fullstack")
|
|
86
|
+
console.log("Start MongoDB or update api/.env. The client proxies /api to the API on port 9149; see README.md.");
|
|
87
|
+
else if (options.type === "api" || options.auth)
|
|
79
88
|
console.log("Start MongoDB or update MONGODB_URI in .env before starting the API.");
|
|
80
89
|
}
|
|
81
90
|
main().catch((error) => {
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { templates } from "./templates.js";
|
|
2
|
+
// Each half remains an ordinary Tilcayo API or Vite app.
|
|
3
|
+
export function fullstackTemplates(options, localPackages) {
|
|
4
|
+
const api = templates({ ...options, name: `${options.name}-api`, type: "api", admin: options.admin }, localPackages);
|
|
5
|
+
const client = templates({ ...options, name: `${options.name}-client`, type: "react" }, localPackages);
|
|
6
|
+
client["vite.config.ts"] = `import react from '@vitejs/plugin-react';
|
|
7
|
+
import { defineConfig } from 'vite';
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
plugins: [react()],
|
|
11
|
+
resolve: { dedupe: ['react', 'react-dom', 'react-redux', '@reduxjs/toolkit', 'react-router'] },
|
|
12
|
+
server: { proxy: { '/api': { target: process.env.API_PROXY_TARGET || 'http://127.0.0.1:9149', changeOrigin: false } } },
|
|
13
|
+
});
|
|
14
|
+
`;
|
|
15
|
+
if (options.auth) {
|
|
16
|
+
api[".env"] += "\nAUTH_COOKIE_SESSION=true\n";
|
|
17
|
+
api[".env.example"] += "\nAUTH_COOKIE_SESSION=true\n";
|
|
18
|
+
client["src/app/auth.ts"] = client["src/app/auth.ts"].replace("createTilcayoAuth({", "createTilcayoAuth({ cookieSession: true,");
|
|
19
|
+
}
|
|
20
|
+
client[".env.example"] = "# Public frontend API base URL\nVITE_API_URL=/api\n";
|
|
21
|
+
client["src/routes.tsx"] = "import { notesRoutes } from './features/notes/notes.routes';\n" + client["src/routes.tsx"].replace("children: [", "children: [\n ...notesRoutes,");
|
|
22
|
+
const files = {};
|
|
23
|
+
for (const [name, source] of Object.entries(api))
|
|
24
|
+
files[`api/${name}`] = source;
|
|
25
|
+
for (const [name, source] of Object.entries(client))
|
|
26
|
+
files[`client/${name}`] = source;
|
|
27
|
+
files["package.json"] = JSON.stringify({
|
|
28
|
+
name: options.name, version: "0.0.1", private: true, type: "module",
|
|
29
|
+
engines: { node: "^22.12.0 || >=24.0.0" },
|
|
30
|
+
workspaces: ["api", "client"],
|
|
31
|
+
tilcayo: { type: "fullstack", auth: options.auth, admin: options.admin ?? false, packageManager: options.packageManager },
|
|
32
|
+
scripts: {
|
|
33
|
+
dev: "node scripts/dev.mjs", "dev:api": "npm run dev --workspace api", "dev:client": "npm run dev --workspace client",
|
|
34
|
+
build: "npm run build --workspaces", start: "npm run start --workspace api", routes: "npm run routes --workspace api",
|
|
35
|
+
...(options.admin ? { 'seed:demo': 'npm run build --workspace api && npm run seed:demo --workspace api', 'seed:demo:remove': 'npm run build --workspace api && npm run seed:demo --workspace api -- --remove' } : {}),
|
|
36
|
+
},
|
|
37
|
+
devDependencies: { "@tilcayo/cli": localPackages.rootCli ?? "^0.0.3" },
|
|
38
|
+
}, null, 2) + "\n";
|
|
39
|
+
files[".gitignore"] = "node_modules/\n**/dist/\n.env\n.env.*\n**/.env\n**/.env.*\n!**/.env.example\n*.log\n";
|
|
40
|
+
if (options.packageManager === "yarn")
|
|
41
|
+
files[".yarnrc.yml"] = "nodeLinker: node-modules\n";
|
|
42
|
+
files["scripts/dev.mjs"] = devScript;
|
|
43
|
+
files["README.md"] = fullstackReadme(options, Object.keys(localPackages).length > 0);
|
|
44
|
+
return files;
|
|
45
|
+
}
|
|
46
|
+
function fullstackReadme(options, local) {
|
|
47
|
+
return `# ${options.name}
|
|
48
|
+
|
|
49
|
+
Tilcayo MongoDB API + React/Vite client${options.auth ? " with authentication" : ""}${options.admin ? " and admin pages" : ""}.
|
|
50
|
+
|
|
51
|
+
Use Node.js 22.12+ or 24+. Start MongoDB and configure api/.env (MONGODB_URI,
|
|
52
|
+
PORT and private auth/mail settings when enabled). Never put secrets in VITE_*.
|
|
53
|
+
|
|
54
|
+
\`\`\`sh
|
|
55
|
+
${options.packageManager} install
|
|
56
|
+
${options.packageManager} run dev
|
|
57
|
+
\`\`\`
|
|
58
|
+
|
|
59
|
+
The client runs on Vite's printed URL and proxies /api to port 9149. If you change
|
|
60
|
+
the API port, set API_PROXY_TARGET for the client dev process or edit
|
|
61
|
+
client/vite.config.ts. Visit /notes for sample CRUD${options.auth ? ", /login to sign in, /register to create an account, /dashboard for your overview and /profile for profile and settings" : ""}${options.admin ? ", and /admin for management" : ""}.
|
|
62
|
+
${options.auth ? "Access tokens stay in memory; an HttpOnly refresh cookie restores the session on reload. Production requires HTTPS and a same-origin API proxy. Configure backend SMTP settings for password recovery.\n" : ""}${options.admin ? "Provision the admin role through trusted server code; registration never grants it.\n" : ""}
|
|
63
|
+
${options.admin ? "Admin users and role filters are at /admin/users. The catalog at /admin/products includes inventory cards and add/edit forms. Product data starts empty; add products in the UI. Protected catalog routes and the Product model are generated in api/src.\n" : ""}
|
|
64
|
+
Generate both halves from this directory:
|
|
65
|
+
|
|
66
|
+
\`\`\`sh
|
|
67
|
+
npx tilcayo make:resource Author name:string --fullstack
|
|
68
|
+
npx tilcayo make:resource Book title:string author:ref:Author year:number? --fullstack
|
|
69
|
+
\`\`\`
|
|
70
|
+
|
|
71
|
+
Register backend routes in api/src/app.ts and spread frontend route arrays into
|
|
72
|
+
client/src/routes.tsx layout children. For admin resources, add their links in
|
|
73
|
+
client/src/features/admin/admin-layout.tsx and nest createBooksRoutes('books',
|
|
74
|
+
'/admin/books') in the admin route module. ${options.tenancy ? "Tenant resource routes require membership and use tenant-scoped models. Add role guards for privileged actions." : "Resource routes are public until you add backend auth.middleware (and auth.requireRole('admin') where appropriate)."}
|
|
75
|
+
|
|
76
|
+
Run ${options.packageManager} run build to build both halves. Production uses
|
|
77
|
+
api/dist/index.js and client/dist; serve the client with SPA fallback and proxy
|
|
78
|
+
/api to the API. npm run start starts the API only. Configure HTTPS on your host.
|
|
79
|
+
The dev runner requires npm on PATH; npm ships with Node.js even if dependencies
|
|
80
|
+
are installed with pnpm or Yarn. Stop both processes with Ctrl+C. Separate
|
|
81
|
+
dev:api and dev:client scripts are available.
|
|
82
|
+
${local ? "\nDependencies link to this source checkout. Keep it available, or replace file/link dependencies with published package versions before sharing the app.\n" : ""}`;
|
|
83
|
+
}
|
|
84
|
+
const devScript = `import { spawn, spawnSync } from 'node:child_process';
|
|
85
|
+
|
|
86
|
+
const windows = process.platform === 'win32';
|
|
87
|
+
const children = ['api', 'client'].map(workspace => spawn('npm', ['run', 'dev', '--workspace', workspace], {
|
|
88
|
+
stdio: 'inherit', shell: windows, windowsHide: true, detached: !windows,
|
|
89
|
+
}));
|
|
90
|
+
let stopping = false;
|
|
91
|
+
function stop(code = 0) {
|
|
92
|
+
if (stopping) return;
|
|
93
|
+
stopping = true;
|
|
94
|
+
for (const child of children) {
|
|
95
|
+
if (!child.pid || child.exitCode !== null) continue;
|
|
96
|
+
if (windows) spawnSync('taskkill', ['/pid', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' });
|
|
97
|
+
else { try { process.kill(-child.pid, 'SIGTERM'); } catch {} }
|
|
98
|
+
}
|
|
99
|
+
process.exitCode = code;
|
|
100
|
+
}
|
|
101
|
+
for (const child of children) {
|
|
102
|
+
child.on('error', error => { console.error(error.message); stop(1); });
|
|
103
|
+
child.on('exit', code => stop(code ?? 0));
|
|
104
|
+
}
|
|
105
|
+
process.on('SIGINT', () => stop());
|
|
106
|
+
process.on('SIGTERM', () => stop());
|
|
107
|
+
`;
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { installTenancy } from "./installTenancy.js";
|
|
1
2
|
import { mkdir, writeFile, readFile, access } from "node:fs/promises";
|
|
2
3
|
import { fileURLToPath } from "node:url";
|
|
3
4
|
import path from "node:path";
|
|
@@ -18,20 +19,23 @@ async function localDependencies(target, options) {
|
|
|
18
19
|
if (manifest.name !== "tilcayo" || manifest.private !== true || !Array.isArray(manifest.workspaces) || !manifest.workspaces.includes("packages/*"))
|
|
19
20
|
return {};
|
|
20
21
|
const dependencies = {};
|
|
21
|
-
for (const name of ["core", "cli", ...(options.auth ? ["auth"] : [])]) {
|
|
22
|
+
for (const name of options.type === "react" ? ["styles", "ui", "react", ...(options.admin ? ["admin"] : [])] : ["core", "cli", ...(options.auth ? ["auth", "cloudinary"] : [])]) {
|
|
22
23
|
const directory = path.join(checkout, "packages", name);
|
|
23
24
|
const pkg = JSON.parse(await readFile(path.join(directory, "package.json"), "utf8"));
|
|
24
25
|
if (pkg.name !== `@tilcayo/${name}`)
|
|
25
26
|
throw new Error(`Invalid local Tilcayo package: ${directory}`);
|
|
27
|
+
const entry = name === "styles" ? "dist/index.css" : "dist/index.js";
|
|
26
28
|
try {
|
|
27
|
-
await access(path.join(directory,
|
|
29
|
+
await access(path.join(directory, entry));
|
|
28
30
|
}
|
|
29
31
|
catch {
|
|
30
|
-
throw new Error(`Build the Tilcayo workspace before creating an application: ${directory}
|
|
32
|
+
throw new Error(`Build the Tilcayo workspace before creating an application: ${directory}/${entry} is missing.`);
|
|
31
33
|
}
|
|
32
34
|
const relative = path.relative(target, directory).split(path.sep).join("/");
|
|
33
35
|
dependencies[name] = `${options.packageManager === "npm" ? "file" : "link"}:${relative}`;
|
|
34
36
|
}
|
|
37
|
+
if (options.type === "react")
|
|
38
|
+
return dependencies;
|
|
35
39
|
const require = createRequire(path.join(checkout, "packages/core/package.json"));
|
|
36
40
|
for (const name of ["zod", ...(options.type === "api" || options.auth ? ["mongoose"] : [])]) {
|
|
37
41
|
const directory = path.dirname(require.resolve(`${name}/package.json`));
|
|
@@ -43,7 +47,18 @@ async function localDependencies(target, options) {
|
|
|
43
47
|
export async function generateApp(options, root = process.cwd()) {
|
|
44
48
|
validateOptions(options);
|
|
45
49
|
const target = path.resolve(root, options.name);
|
|
46
|
-
|
|
50
|
+
let packages;
|
|
51
|
+
if (options.type === "fullstack") {
|
|
52
|
+
const [api, client, app] = await Promise.all([
|
|
53
|
+
localDependencies(path.join(target, "api"), { ...options, type: "api" }),
|
|
54
|
+
localDependencies(path.join(target, "client"), { ...options, type: "react" }),
|
|
55
|
+
localDependencies(target, { ...options, type: "api" }),
|
|
56
|
+
]);
|
|
57
|
+
packages = { ...api, ...client, ...(app.cli ? { rootCli: app.cli } : {}) };
|
|
58
|
+
}
|
|
59
|
+
else
|
|
60
|
+
packages = await localDependencies(target, options);
|
|
61
|
+
const sources = templates(options, packages);
|
|
47
62
|
try {
|
|
48
63
|
await mkdir(target);
|
|
49
64
|
}
|
|
@@ -55,12 +70,27 @@ export async function generateApp(options, root = process.cwd()) {
|
|
|
55
70
|
for (const [filename, source] of Object.entries(sources)) {
|
|
56
71
|
const destination = path.join(target, filename);
|
|
57
72
|
await mkdir(path.dirname(destination), { recursive: true });
|
|
58
|
-
|
|
73
|
+
const content = source.startsWith("__TILCAYO_BASE64__:") ? Buffer.from(source.slice("__TILCAYO_BASE64__:".length), "base64") : source;
|
|
74
|
+
await writeFile(destination, content, { flag: "wx", ...(filename === ".env" ? { mode: 0o600 } : {}) });
|
|
59
75
|
}
|
|
60
76
|
if (options.type === "api")
|
|
61
77
|
await make(["make:resource", "Note", "title:string", "content?:string"], target);
|
|
62
|
-
if (options.
|
|
78
|
+
if (options.type === "fullstack") {
|
|
79
|
+
await make(["make:resource", "Note", "title:string", "content?:string", "--fullstack"], target);
|
|
80
|
+
if (options.auth) {
|
|
81
|
+
await make(["make:auth"], path.join(target, "api"));
|
|
82
|
+
const authFile = path.join(target, "api/src/auth.ts");
|
|
83
|
+
const source = await readFile(authFile, "utf8");
|
|
84
|
+
await writeFile(authFile, source.replace('cookieSession: process.env.AUTH_COOKIE_SESSION === "true"', "cookieSession: true"));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (options.auth && options.type !== "react" && options.type !== "fullstack")
|
|
63
88
|
await make(["make:auth"], target);
|
|
89
|
+
if (options.tenancy) {
|
|
90
|
+
await installTenancy(target);
|
|
91
|
+
const readme = path.join(target, "README.md");
|
|
92
|
+
await writeFile(readme, await readFile(readme, "utf8") + "\nTenancy enabled: set TENANCY_SUPERADMIN_EMAIL and TENANCY_SUPERADMIN_PASSWORD for npm run tenancy:setup. Setup provisions only the Super Admin. Create tenants and assign their administrators in Tenant administration on the main domain; no default tenant or automatic record migration is created. Use the Vite port in the URL. Configure TENANCY_BASE_DOMAIN, wildcard DNS, HTTPS and a same-origin Host-preserving API proxy for production. Workspace sessions are host-only. Platform admins manage tenants; tenant roles belong to memberships.\n");
|
|
93
|
+
}
|
|
64
94
|
return target;
|
|
65
95
|
}
|
|
66
96
|
function run(command, args, cwd, shell = false) {
|
|
@@ -76,11 +106,9 @@ export async function installApp(target, manager) {
|
|
|
76
106
|
try {
|
|
77
107
|
// Both the command and its arguments are fixed, not user-supplied shell text.
|
|
78
108
|
await run(manager, ["install"], target, process.platform === "win32");
|
|
79
|
-
|
|
80
|
-
const cli = path.join(path.dirname(require.resolve("@tilcayo/cli")), "bin.js");
|
|
81
|
-
await run(process.execPath, [cli, "build"], target);
|
|
109
|
+
await run(manager, ["run", "build"], target, process.platform === "win32");
|
|
82
110
|
}
|
|
83
111
|
catch {
|
|
84
|
-
throw new Error(`Project created at ${target}, but dependency installation or build failed. Check that ${manager} is installed and Tilcayo packages are available in your registry, then run ${manager} install and
|
|
112
|
+
throw new Error(`Project created at ${target}, but dependency installation or build failed. Check that ${manager} is installed and Tilcayo packages are available in your registry, then run ${manager} install and ${manager} run build inside the project.`);
|
|
85
113
|
}
|
|
86
114
|
}
|