@mostajs/setup 1.0.0 → 1.1.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.
@@ -0,0 +1,69 @@
1
+ // @mosta/setup — Dynamic npm module discovery
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ import { exec } from 'child_process';
4
+ import { promisify } from 'util';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { MODULES } from '../data/module-definitions';
8
+ const execAsync = promisify(exec);
9
+ /**
10
+ * Discover @mostajs packages from npm registry and merge with static list.
11
+ *
12
+ * - Known modules keep their rich metadata (required, dependsOn, icon)
13
+ * - New packages found on npm are added with `discovered: true`
14
+ * - Detects which packages are already installed in node_modules/
15
+ * - Falls back to static list if npm search fails (offline, timeout)
16
+ */
17
+ export async function discoverNpmModules() {
18
+ const staticModules = [...MODULES];
19
+ const modulesByPackage = new Map(staticModules.map((m) => [m.packageName, m]));
20
+ // Try npm search with 10s timeout
21
+ try {
22
+ const { stdout } = await execAsync('npm search @mostajs --json', {
23
+ timeout: 10_000,
24
+ });
25
+ const npmResults = JSON.parse(stdout);
26
+ for (const pkg of npmResults) {
27
+ // Skip if not @mostajs scoped
28
+ if (!pkg.name.startsWith('@mostajs/'))
29
+ continue;
30
+ // Skip if already in static list
31
+ if (modulesByPackage.has(pkg.name))
32
+ continue;
33
+ // Derive key from package name: @mostajs/foo-bar -> foo-bar
34
+ const key = pkg.name.replace('@mostajs/', '');
35
+ staticModules.push({
36
+ key,
37
+ packageName: pkg.name,
38
+ label: pkg.name,
39
+ description: pkg.description || '',
40
+ icon: '📦',
41
+ default: false,
42
+ discovered: true,
43
+ });
44
+ }
45
+ }
46
+ catch {
47
+ // npm search failed (offline, timeout) — use static list only
48
+ }
49
+ // Detect installed packages: check node_modules/ and local packages/
50
+ const installed = [];
51
+ const nodeModulesBase = path.resolve(process.cwd(), 'node_modules', '@mostajs');
52
+ const packagesDir = path.resolve(process.cwd(), 'packages');
53
+ for (const mod of staticModules) {
54
+ // Check node_modules/@mostajs/<name>
55
+ const npmDir = mod.packageName.replace('@mostajs/', '');
56
+ const nmPath = path.join(nodeModulesBase, npmDir);
57
+ // Check local packages/<localDir>
58
+ const localPath = mod.localDir ? path.join(packagesDir, mod.localDir) : null;
59
+ try {
60
+ if (fs.existsSync(nmPath) || (localPath && fs.existsSync(localPath))) {
61
+ installed.push(mod.key);
62
+ }
63
+ }
64
+ catch {
65
+ // ignore
66
+ }
67
+ }
68
+ return { modules: staticModules, installed };
69
+ }
@@ -1,4 +1,4 @@
1
- import type { DialectType } from '../types';
1
+ import type { DialectType } from '../types/index';
2
2
  export interface EnvWriterOptions {
3
3
  dialect: DialectType;
4
4
  uri: string;
@@ -1,57 +1,18 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.writeEnvLocal = writeEnvLocal;
40
1
  // @mosta/setup — Write/update .env.local
41
2
  // Author: Dr Hamid MADANI drmdh@msn.com
42
- const fs_1 = __importDefault(require("fs"));
43
- const path_1 = __importDefault(require("path"));
3
+ import fs from 'fs';
4
+ import path from 'path';
44
5
  /**
45
6
  * Write or update .env.local with DB_DIALECT + SGBD_URI.
46
7
  * Preserves commented lines. Returns true if dialect changed (needs restart).
47
8
  */
48
- async function writeEnvLocal(options) {
9
+ export async function writeEnvLocal(options) {
49
10
  const { dialect, uri, extraVars, port = 3000 } = options;
50
- const envPath = path_1.default.resolve(process.cwd(), '.env.local');
11
+ const envPath = path.resolve(process.cwd(), '.env.local');
51
12
  let content = '';
52
13
  let previousDialect = null;
53
14
  try {
54
- content = fs_1.default.readFileSync(envPath, 'utf-8');
15
+ content = fs.readFileSync(envPath, 'utf-8');
55
16
  const match = content.match(/^DB_DIALECT=(.+)$/m);
56
17
  if (match)
57
18
  previousDialect = match[1].trim();
@@ -79,7 +40,7 @@ async function writeEnvLocal(options) {
79
40
  }
80
41
  else {
81
42
  // Fresh .env.local
82
- const { randomBytes } = await Promise.resolve().then(() => __importStar(require('crypto')));
43
+ const { randomBytes } = await import('crypto');
83
44
  const secret = randomBytes(32).toString('base64');
84
45
  const lines = [
85
46
  `DB_DIALECT=${dialect}`,
@@ -102,7 +63,7 @@ async function writeEnvLocal(options) {
102
63
  ];
103
64
  content = lines.join('\n') + '\n';
104
65
  }
105
- fs_1.default.writeFileSync(envPath, content, 'utf-8');
66
+ fs.writeFileSync(envPath, content, 'utf-8');
106
67
  return previousDialect !== null && previousDialect !== dialect;
107
68
  }
108
69
  function upsertEnvLine(content, key, value) {
@@ -1,4 +1,4 @@
1
- import type { InstallConfig, MostaSetupConfig } from '../types';
1
+ import type { InstallConfig, MostaSetupConfig } from '../types/index';
2
2
  /**
3
3
  * Check if the app needs initial setup (0 users in DB).
4
4
  * Provide a countUsers function from your app.
package/dist/lib/setup.js CHANGED
@@ -1,49 +1,12 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.needsSetup = needsSetup;
37
- exports.runInstall = runInstall;
38
1
  // @mosta/setup — Core setup logic
39
2
  // Author: Dr Hamid MADANI drmdh@msn.com
40
- const compose_uri_1 = require("./compose-uri");
41
- const env_writer_1 = require("./env-writer");
3
+ import { composeDbUri } from './compose-uri';
4
+ import { writeEnvLocal } from './env-writer';
42
5
  /**
43
6
  * Check if the app needs initial setup (0 users in DB).
44
7
  * Provide a countUsers function from your app.
45
8
  */
46
- async function needsSetup(countUsers) {
9
+ export async function needsSetup(countUsers) {
47
10
  try {
48
11
  const count = await countUsers();
49
12
  return count === 0;
@@ -62,14 +25,18 @@ async function needsSetup(countUsers) {
62
25
  * 5. Create first admin user
63
26
  * 6. Run optional seeds
64
27
  */
65
- async function runInstall(installConfig, setupConfig) {
28
+ export async function runInstall(installConfig, setupConfig) {
66
29
  try {
67
30
  // 1. Compose URI and write .env.local
68
- const uri = (0, compose_uri_1.composeDbUri)(installConfig.dialect, installConfig.db);
69
- const needsRestart = await (0, env_writer_1.writeEnvLocal)({
31
+ const uri = composeDbUri(installConfig.dialect, installConfig.db);
32
+ const extraVars = { ...setupConfig.extraEnvVars };
33
+ if (installConfig.modules?.length) {
34
+ extraVars['MOSTAJS_MODULES'] = installConfig.modules.join(',');
35
+ }
36
+ const needsRestart = await writeEnvLocal({
70
37
  dialect: installConfig.dialect,
71
38
  uri,
72
- extraVars: setupConfig.extraEnvVars,
39
+ extraVars,
73
40
  port: setupConfig.defaultPort,
74
41
  });
75
42
  // 2. Set process.env in-memory
@@ -79,7 +46,7 @@ async function runInstall(installConfig, setupConfig) {
79
46
  process.env.DB_SCHEMA_STRATEGY = 'update';
80
47
  }
81
48
  // 3. Disconnect existing dialect singleton
82
- const { disconnectDialect } = await Promise.resolve().then(() => __importStar(require('@mostajs/orm')));
49
+ const { disconnectDialect } = await import('@mostajs/orm');
83
50
  await disconnectDialect();
84
51
  // 4. Seed RBAC
85
52
  const seeded = [];
@@ -89,7 +56,7 @@ async function runInstall(installConfig, setupConfig) {
89
56
  }
90
57
  // 5. Create admin user
91
58
  if (setupConfig.createAdmin) {
92
- const bcrypt = await Promise.resolve().then(() => __importStar(require('bcryptjs')));
59
+ const bcrypt = await import('bcryptjs');
93
60
  const hashedPassword = await bcrypt.hash(installConfig.admin.password, 12);
94
61
  await setupConfig.createAdmin({
95
62
  email: installConfig.admin.email,
@@ -26,7 +26,9 @@ export interface InstallConfig {
26
26
  lastName: string;
27
27
  };
28
28
  seed?: SeedOptions;
29
+ modules?: string[];
29
30
  }
31
+ export type { ModuleDefinition } from '../data/module-definitions';
30
32
  export interface SeedOptions {
31
33
  [key: string]: boolean;
32
34
  }
@@ -43,8 +45,6 @@ export interface MostaSetupConfig {
43
45
  appName: string;
44
46
  /** Default port (default: 3000) */
45
47
  defaultPort?: number;
46
- /** Enabled dialects (default: all 13) */
47
- enabledDialects?: DialectType[];
48
48
  /** Callback to seed RBAC (permissions, roles, categories) */
49
49
  seedRBAC?: () => Promise<void>;
50
50
  /** Create first admin user — called with hashed password */
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  // @mosta/setup — Types
3
2
  // Author: Dr Hamid MADANI drmdh@msn.com
4
- Object.defineProperty(exports, "__esModule", { value: true });
3
+ export {};
package/package.json CHANGED
@@ -1,17 +1,37 @@
1
1
  {
2
2
  "name": "@mostajs/setup",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Reusable setup wizard module — multi-dialect DB configuration, .env.local writer, seed runner",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "MIT",
7
+ "type": "module",
7
8
  "main": "dist/index.js",
8
9
  "types": "dist/index.d.ts",
9
10
  "exports": {
10
11
  ".": {
11
12
  "types": "./dist/index.d.ts",
12
13
  "import": "./dist/index.js",
13
- "require": "./dist/index.js",
14
14
  "default": "./dist/index.js"
15
+ },
16
+ "./lib/*": {
17
+ "types": "./dist/lib/*.d.ts",
18
+ "import": "./dist/lib/*.js",
19
+ "default": "./dist/lib/*.js"
20
+ },
21
+ "./data/*": {
22
+ "types": "./dist/data/*.d.ts",
23
+ "import": "./dist/data/*.js",
24
+ "default": "./dist/data/*.js"
25
+ },
26
+ "./api/*": {
27
+ "types": "./dist/api/*.d.ts",
28
+ "import": "./dist/api/*.js",
29
+ "default": "./dist/api/*.js"
30
+ },
31
+ "./types": {
32
+ "types": "./dist/types/index.d.ts",
33
+ "import": "./dist/types/index.js",
34
+ "default": "./dist/types/index.js"
15
35
  }
16
36
  },
17
37
  "files": [
@@ -44,27 +64,9 @@
44
64
  "@mostajs/orm": "^1.0.0",
45
65
  "bcryptjs": "^2.4.3"
46
66
  },
47
- "peerDependencies": {
48
- "next": ">=14",
49
- "react": ">=18"
50
- },
51
- "peerDependenciesMeta": {
52
- "next": {
53
- "optional": true
54
- },
55
- "react": {
56
- "optional": true
57
- }
58
- },
59
- "optionalDependencies": {
60
- "mongoose": "^8.0.0"
61
- },
62
67
  "devDependencies": {
63
68
  "@types/bcryptjs": "^2.4.0",
64
69
  "@types/node": "^25.3.3",
65
- "@types/react": "^19.0.0",
66
- "next": "^15.0.0",
67
- "react": "^19.0.0",
68
70
  "typescript": "^5.6.0"
69
71
  }
70
72
  }