@fluixi/cli 0.1.0-alpha.82 → 0.1.0-alpha.83

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 (57) hide show
  1. package/README.md +21 -3
  2. package/dist/add.cjs +590 -0
  3. package/dist/add.d.ts +10 -0
  4. package/dist/add.d.ts.map +1 -0
  5. package/dist/add.js +107 -0
  6. package/dist/add.mjs +562 -0
  7. package/dist/check.cjs +102 -0
  8. package/dist/check.d.ts +13 -0
  9. package/dist/check.d.ts.map +1 -0
  10. package/dist/check.js +88 -0
  11. package/dist/check.mjs +76 -0
  12. package/dist/cli.cjs +977 -78
  13. package/dist/cli.js +44 -2
  14. package/dist/cli.mjs +976 -77
  15. package/dist/doctor.cjs +119 -0
  16. package/dist/doctor.d.ts +13 -0
  17. package/dist/doctor.d.ts.map +1 -0
  18. package/dist/doctor.js +114 -0
  19. package/dist/doctor.mjs +92 -0
  20. package/dist/generate.cjs +614 -65
  21. package/dist/generate.d.ts.map +1 -1
  22. package/dist/generate.js +118 -48
  23. package/dist/generate.mjs +619 -64
  24. package/dist/index.cjs +1 -1
  25. package/dist/index.mjs +1 -1
  26. package/dist/report.cjs +54 -0
  27. package/dist/report.d.ts +5 -0
  28. package/dist/report.d.ts.map +1 -0
  29. package/dist/report.js +31 -0
  30. package/dist/report.mjs +28 -0
  31. package/dist/scaffold/auth.cjs +422 -0
  32. package/dist/scaffold/auth.d.ts +39 -0
  33. package/dist/scaffold/auth.d.ts.map +1 -0
  34. package/dist/scaffold/auth.js +137 -0
  35. package/dist/scaffold/auth.mjs +389 -0
  36. package/dist/scaffold/edit.cjs +153 -0
  37. package/dist/scaffold/edit.d.ts +35 -0
  38. package/dist/scaffold/edit.d.ts.map +1 -0
  39. package/dist/scaffold/edit.js +140 -0
  40. package/dist/scaffold/edit.mjs +124 -0
  41. package/dist/scaffold/index.cjs +610 -0
  42. package/dist/scaffold/index.d.ts +10 -0
  43. package/dist/scaffold/index.d.ts.map +1 -0
  44. package/dist/scaffold/index.js +9 -0
  45. package/dist/scaffold/index.mjs +569 -0
  46. package/dist/scaffold/prompt.cjs +108 -0
  47. package/dist/scaffold/prompt.d.ts +9 -0
  48. package/dist/scaffold/prompt.d.ts.map +1 -0
  49. package/dist/scaffold/prompt.js +95 -0
  50. package/dist/scaffold/prompt.mjs +81 -0
  51. package/dist/scaffold/templates.cjs +274 -0
  52. package/dist/scaffold/templates.d.ts +26 -0
  53. package/dist/scaffold/templates.d.ts.map +1 -0
  54. package/dist/scaffold/templates.js +216 -0
  55. package/dist/scaffold/templates.mjs +229 -0
  56. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  57. package/package.json +19 -3
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AAyMA,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAmCjF"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../src/generate.ts"],"names":[],"mappings":"AA8MA,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAgGjF"}
package/dist/generate.js CHANGED
@@ -4,18 +4,32 @@
4
4
  * fluixi g route about → src/routes/about.tsx (a file route)
5
5
  * fluixi g route blog/[id] → src/routes/blog/[id].tsx
6
6
  * fluixi g middleware → src/middleware.ts
7
+ * fluixi g service Orders → src/services/orders.ts, listed in src/app.config.ts
8
+ * fluixi g auth oauth2 → env config, auth service, callback and login routes
7
9
  */
8
10
  import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'node:fs';
9
11
  import { resolve, dirname, relative, basename } from 'node:path';
10
12
  import { c } from '@fluixi/start/ui';
11
- const GEN_USAGE = `Usage: fluixi generate <kind> <name>
13
+ import { envPublicNames, ensureAuthEnv, registerModule, ScaffoldError, setupBetterAuth, setupOAuth2, templates as t, writeOnce as writeOnceTo, importPath, } from './scaffold/index.js';
14
+ import { report, reportAuth } from './report.js';
15
+ const GEN_USAGE = `Usage: fluixi generate <kind> [name] [options]
12
16
 
13
17
  Kinds:
14
- component <Name> a reusable component → src/components/<Name>.{tsx,ts}
15
- route <path> a file-based route page → src/routes/<path>.{tsx,ts}
16
- middleware the request middleware file → src/middleware.ts
17
- auth @fluixi/auth wiring → auth.config + auth.client + middleware + /api/auth route
18
+ component <Name> a reusable component → src/components/<Name>.{tsx,ts}
19
+ route <path> a file-based route page → src/routes/<path>.{tsx,ts}
20
+ middleware the request middleware file → src/middleware.ts
21
+ env typed config from env.public → src/config/env.ts
22
+ module <name> an app module → src/services/<name>/<name>.module.ts
23
+ service <Name> [--server] a service, token and module → src/services/<name>.ts (.server.ts)
24
+ api <path> [--role <role>] an API route, guarded by role → src/api/<path>.ts
25
+ server-fn <name> a "use server" function → src/server/<name>.ts
26
+ auth oauth2 [--provider <p>] [--tokens browser|server]
27
+ @fluixi/oauth2 wiring: env config, service, routes, .env.example
28
+ (fluixi add auth does the same, asks, and installs)
29
+ auth better-auth @fluixi/auth wiring → auth.config + auth.client + middleware + /api/auth
18
30
 
31
+ Modules and services are listed in src/app.config.ts, or app.config.server.ts for --server,
32
+ which is created when missing. Providers: ${t.PROVIDERS.join(', ')}.
19
33
  Component/route syntax follows the project (JSX → .tsx, html\`\` → .ts); override with --jsx / --html.
20
34
  `;
21
35
  /**
@@ -76,6 +90,27 @@ function resolveHint(Name, ext) {
76
90
  console.log(` ${c.gray(`resolve: [{ match: /^([A-Z]\\w+)$/, module: '/src/components/$1.${ext}' }]`)}`);
77
91
  console.log(` ${c.gray('one pattern covers the whole folder. Importing it works too — including with')} ${c.cyan('load:')}`);
78
92
  }
93
+ /** The value of `--name value` or `--name=value`. */
94
+ function option(name) {
95
+ const args = process.argv;
96
+ const i = args.indexOf(`--${name}`);
97
+ if (i !== -1 && args[i + 1] && !args[i + 1].startsWith('--'))
98
+ return args[i + 1];
99
+ return args.find((a) => a.startsWith(`--${name}=`))?.slice(name.length + 3);
100
+ }
101
+ const kebab = (s) => s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
102
+ /** Write a file the app may already have, keeping theirs. Returns whether it wrote. */
103
+ function writeOnce(file, content) {
104
+ const steps = [];
105
+ const wrote = writeOnceTo(steps, file, content);
106
+ report(steps);
107
+ return wrote;
108
+ }
109
+ function registerIn(server, file, name, entry = name) {
110
+ const steps = [];
111
+ registerModule(steps, process.cwd(), server, file, name, entry);
112
+ report(steps);
113
+ }
79
114
  function write(file, content) {
80
115
  const rel = relative(process.cwd(), file);
81
116
  if (existsSync(file)) {
@@ -140,44 +175,6 @@ export default defineMiddleware([
140
175
  },
141
176
  ]);
142
177
  `;
143
- const authConfig = () => `import { betterAuth } from 'better-auth';
144
- import { defineAuth } from '@fluixi/auth/server';
145
-
146
- export type AppUser = { id: string; email: string; name?: string; role?: string };
147
-
148
- // Better Auth owns the audited primitives; defineAuth registers it with @fluixi/auth.
149
- export const auth = defineAuth<AppUser>(
150
- betterAuth({
151
- // database: <your adapter>,
152
- emailAndPassword: { enabled: true },
153
- // socialProviders: { google: { … }, github: { … } },
154
- } as Parameters<typeof betterAuth>[0]),
155
- );
156
- `;
157
- const authClient = () => `import { createAuthClient } from 'better-auth/client';
158
- import { createAuthHooks } from '@fluixi/auth/client';
159
- import type { AppUser } from './auth.config.js';
160
-
161
- const client = createAuthClient({ baseURL: '/api/auth' });
162
-
163
- // Or, to reuse an existing reactive session: createAuthHooks({ session: yourSessionStore }).
164
- export const { useUser, useSession, useAuth, useSignIn, useSignOut, protectedRoute } =
165
- createAuthHooks<AppUser>({ client, loginPath: '/login' });
166
- `;
167
- const authMiddleware = () => `import { defineMiddleware } from '@fluixi/start';
168
- import { authMiddleware } from '@fluixi/auth/server';
169
- import './auth.config.js';
170
-
171
- export default defineMiddleware([authMiddleware()]);
172
- `;
173
- const authRoute = () => `import { mountAuthRoutes } from '@fluixi/auth/server';
174
- import '../../../auth.config.js';
175
-
176
- const handler = mountAuthRoutes();
177
- export const GET = handler;
178
- export const POST = handler;
179
- export default handler;
180
- `;
181
178
  export function generate(kind, name) {
182
179
  switch (kind) {
183
180
  case 'component':
@@ -204,18 +201,91 @@ export function generate(kind, name) {
204
201
  case 'm':
205
202
  write(resolve('src/middleware.ts'), middleware());
206
203
  break;
204
+ case 'env': {
205
+ const names = (envPublicNames(resolve('fluixi.config.ts')) ?? []).filter((n) => !n.startsWith('AUTH_'));
206
+ write(resolve('src/config/env.ts'), t.envConfig(names));
207
+ break;
208
+ }
209
+ case 'module': {
210
+ if (!name)
211
+ return fail('module name required, e.g. `fluixi g module billing`');
212
+ const slug = kebab(name);
213
+ const file = resolve('src/services', slug, `${slug}.module.ts`);
214
+ write(file, t.moduleFile(pascal(name), slug));
215
+ registerIn(false, file, `${pascal(name)}Module`);
216
+ break;
217
+ }
218
+ case 'service':
219
+ case 's': {
220
+ if (!name)
221
+ return fail('service name required, e.g. `fluixi g service Orders`');
222
+ const Name = pascal(name).replace(/Service$/, '') || 'App';
223
+ const server = process.argv.includes('--server');
224
+ const file = resolve('src/services', `${kebab(Name)}${server ? '.server' : ''}.ts`);
225
+ write(file, t.serviceFile(Name, kebab(Name), server));
226
+ registerIn(server, file, `${Name}Module`);
227
+ break;
228
+ }
229
+ case 'api': {
230
+ if (!name)
231
+ return fail('api path required, e.g. `fluixi g api users/[id]`');
232
+ const file = resolve('src/api', `${name.replace(/\.ts$/, '')}.ts`);
233
+ const role = option('role');
234
+ if (!role) {
235
+ write(file, t.apiRoute());
236
+ break;
237
+ }
238
+ const verify = resolve('src/services/verify.server.ts');
239
+ write(file, t.guardedApiRoute(importPath(file, verify), role));
240
+ writeOnce(verify, t.verifyServer());
241
+ {
242
+ const steps = [];
243
+ ensureAuthEnv(steps, process.cwd());
244
+ report(steps);
245
+ }
246
+ break;
247
+ }
248
+ case 'server-fn': {
249
+ if (!name)
250
+ return fail('function name required, e.g. `fluixi g server-fn getUser`');
251
+ const fn = name.replace(/[^a-zA-Z0-9]+(.)/g, (_, ch) => ch.toUpperCase());
252
+ write(resolve('src/server', `${fn}.ts`), t.serverFn(fn));
253
+ break;
254
+ }
207
255
  case 'auth':
208
- write(resolve('src/auth.config.ts'), authConfig());
209
- write(resolve('src/auth.client.ts'), authClient());
210
- write(resolve('src/middleware.ts'), authMiddleware());
211
- write(resolve('src/routes/api/auth/[...all].ts'), authRoute());
212
- console.log(` ${c.gray('next: install')} ${c.cyan('better-auth')}${c.gray(', set your DB adapter in auth.config.ts, and forward `request` in entry-server')}`);
256
+ try {
257
+ if (name === 'oauth2') {
258
+ reportAuth(setupOAuth2(process.cwd(), {
259
+ provider: option('provider'),
260
+ tokens: tokensOption(),
261
+ format: detectFormat(),
262
+ }), 'install @fluixi/oauth2');
263
+ }
264
+ else if (!name || name === 'better-auth') {
265
+ reportAuth(setupBetterAuth(process.cwd()), 'install @fluixi/auth and better-auth');
266
+ }
267
+ else {
268
+ return fail(`unknown auth kind ${name}: oauth2 or better-auth`);
269
+ }
270
+ }
271
+ catch (error) {
272
+ if (error instanceof ScaffoldError)
273
+ return fail(error.message);
274
+ throw error;
275
+ }
213
276
  break;
214
277
  default:
215
278
  console.error(GEN_USAGE);
216
279
  process.exit(1);
217
280
  }
218
281
  }
282
+ function tokensOption() {
283
+ const tokens = option('tokens');
284
+ if (tokens !== undefined && tokens !== 'browser' && tokens !== 'server') {
285
+ fail('--tokens is browser or server');
286
+ }
287
+ return tokens;
288
+ }
219
289
  function fail(msg) {
220
290
  console.error(` ${c.red('✗')} ${msg}`);
221
291
  process.exit(1);