@suiteportal/cli 0.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.
package/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # @suiteportal/cli
2
+
3
+ CLI for SuitePortal NetSuite ORM. Introspect your NetSuite account, generate typed clients, and manage your ORM configuration.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @suiteportal/cli
9
+ # or use npx
10
+ npx @suiteportal/cli
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```bash
16
+ # Initialize a new SuitePortal project
17
+ suiteportal init
18
+
19
+ # Introspect your NetSuite account schema
20
+ suiteportal introspect
21
+
22
+ # Generate typed client from schema
23
+ suiteportal generate
24
+ ```
25
+
26
+ ## Commands
27
+
28
+ | Command | Description |
29
+ |---------|-------------|
30
+ | `init` | Initialize a new SuitePortal project with config file |
31
+ | `introspect` | Connect to NetSuite and discover schema |
32
+ | `generate` | Generate TypeScript types and client from schema |
33
+
34
+ ## Configuration
35
+
36
+ Credentials are loaded from `.env`:
37
+
38
+ ```env
39
+ NETSUITE_ACCOUNT_ID=1234567_SB1
40
+ NETSUITE_CONSUMER_KEY=...
41
+ NETSUITE_CONSUMER_SECRET=...
42
+ NETSUITE_TOKEN_ID=...
43
+ NETSUITE_TOKEN_SECRET=...
44
+ ```
45
+
46
+ ## Documentation
47
+
48
+ Full docs at [suiteportal.dev](https://suiteportal.dev)
49
+
50
+ ## License
51
+
52
+ MIT
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/logger.ts
4
+ var RESET = "\x1B[0m";
5
+ var BOLD = "\x1B[1m";
6
+ var DIM = "\x1B[2m";
7
+ var RED = "\x1B[31m";
8
+ var GREEN = "\x1B[32m";
9
+ var YELLOW = "\x1B[33m";
10
+ var BLUE = "\x1B[34m";
11
+ var CYAN = "\x1B[36m";
12
+ function format(color, prefix, message) {
13
+ return `${color}${BOLD}${prefix}${RESET} ${message}`;
14
+ }
15
+ var logger = {
16
+ info(message) {
17
+ console.log(format(BLUE, "info", message));
18
+ },
19
+ success(message) {
20
+ console.log(format(GREEN, " \u2713 ", message));
21
+ },
22
+ warn(message) {
23
+ console.log(format(YELLOW, "warn", message));
24
+ },
25
+ error(message) {
26
+ console.error(format(RED, "error", message));
27
+ },
28
+ step(message) {
29
+ console.log(format(CYAN, " \u2192 ", message));
30
+ },
31
+ dim(message) {
32
+ console.log(`${DIM}${message}${RESET}`);
33
+ },
34
+ banner() {
35
+ console.log(`
36
+ ${BOLD}${CYAN} SuitePortal${RESET} ${DIM}v0.1.0${RESET}
37
+ `);
38
+ }
39
+ };
40
+
41
+ export {
42
+ logger
43
+ };
44
+ //# sourceMappingURL=chunk-276NVAI2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/logger.ts"],"sourcesContent":["const RESET = '\\x1b[0m';\nconst BOLD = '\\x1b[1m';\nconst DIM = '\\x1b[2m';\nconst RED = '\\x1b[31m';\nconst GREEN = '\\x1b[32m';\nconst YELLOW = '\\x1b[33m';\nconst BLUE = '\\x1b[34m';\nconst CYAN = '\\x1b[36m';\n\nfunction format(color: string, prefix: string, message: string): string {\n return `${color}${BOLD}${prefix}${RESET} ${message}`;\n}\n\nexport const logger = {\n info(message: string): void {\n console.log(format(BLUE, 'info', message));\n },\n\n success(message: string): void {\n console.log(format(GREEN, ' ✓ ', message));\n },\n\n warn(message: string): void {\n console.log(format(YELLOW, 'warn', message));\n },\n\n error(message: string): void {\n console.error(format(RED, 'error', message));\n },\n\n step(message: string): void {\n console.log(format(CYAN, ' → ', message));\n },\n\n dim(message: string): void {\n console.log(`${DIM}${message}${RESET}`);\n },\n\n banner(): void {\n console.log(`\\n${BOLD}${CYAN} SuitePortal${RESET} ${DIM}v0.1.0${RESET}\\n`);\n },\n};\n"],"mappings":";;;AAAA,IAAM,QAAQ;AACd,IAAM,OAAO;AACb,IAAM,MAAM;AACZ,IAAM,MAAM;AACZ,IAAM,QAAQ;AACd,IAAM,SAAS;AACf,IAAM,OAAO;AACb,IAAM,OAAO;AAEb,SAAS,OAAO,OAAe,QAAgB,SAAyB;AACtE,SAAO,GAAG,KAAK,GAAG,IAAI,GAAG,MAAM,GAAG,KAAK,IAAI,OAAO;AACpD;AAEO,IAAM,SAAS;AAAA,EACpB,KAAK,SAAuB;AAC1B,YAAQ,IAAI,OAAO,MAAM,QAAQ,OAAO,CAAC;AAAA,EAC3C;AAAA,EAEA,QAAQ,SAAuB;AAC7B,YAAQ,IAAI,OAAO,OAAO,aAAQ,OAAO,CAAC;AAAA,EAC5C;AAAA,EAEA,KAAK,SAAuB;AAC1B,YAAQ,IAAI,OAAO,QAAQ,QAAQ,OAAO,CAAC;AAAA,EAC7C;AAAA,EAEA,MAAM,SAAuB;AAC3B,YAAQ,MAAM,OAAO,KAAK,SAAS,OAAO,CAAC;AAAA,EAC7C;AAAA,EAEA,KAAK,SAAuB;AAC1B,YAAQ,IAAI,OAAO,MAAM,aAAQ,OAAO,CAAC;AAAA,EAC3C;AAAA,EAEA,IAAI,SAAuB;AACzB,YAAQ,IAAI,GAAG,GAAG,GAAG,OAAO,GAAG,KAAK,EAAE;AAAA,EACxC;AAAA,EAEA,SAAe;AACb,YAAQ,IAAI;AAAA,EAAK,IAAI,GAAG,IAAI,gBAAgB,KAAK,IAAI,GAAG,SAAS,KAAK;AAAA,CAAI;AAAA,EAC5E;AACF;","names":[]}
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/utils/config-loader.ts
4
+ import { existsSync } from "fs";
5
+ import { resolve } from "path";
6
+ import { pathToFileURL } from "url";
7
+ var CONFIG_NAMES = [
8
+ "suiteportal.config.ts",
9
+ "suiteportal.config.js",
10
+ "suiteportal.config.mjs"
11
+ ];
12
+ async function loadConfig(cwd) {
13
+ const dir = cwd ?? process.cwd();
14
+ for (const name of CONFIG_NAMES) {
15
+ const fullPath = resolve(dir, name);
16
+ if (existsSync(fullPath)) {
17
+ try {
18
+ const fileUrl = pathToFileURL(fullPath).href;
19
+ const mod = await import(fileUrl);
20
+ return mod.default ?? mod;
21
+ } catch {
22
+ return {};
23
+ }
24
+ }
25
+ }
26
+ return {};
27
+ }
28
+ function toIntrospectOptions(config) {
29
+ return {
30
+ recordTypes: config.recordTypes,
31
+ outputDir: config.outputDir ?? ".suiteportal",
32
+ fetchDetails: config.fetchDetails ?? true,
33
+ includeCustomRecords: config.includeCustomRecords ?? true
34
+ };
35
+ }
36
+
37
+ export {
38
+ loadConfig,
39
+ toIntrospectOptions
40
+ };
41
+ //# sourceMappingURL=chunk-ADG2JPAJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/config-loader.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { IntrospectOptions } from '@suiteportal/introspector';\n\nconst CONFIG_NAMES = [\n 'suiteportal.config.ts',\n 'suiteportal.config.js',\n 'suiteportal.config.mjs',\n];\n\nexport interface SuitePortalConfig {\n recordTypes?: string[];\n outputDir?: string;\n fetchDetails?: boolean;\n includeCustomRecords?: boolean;\n}\n\n/**\n * Load the suiteportal config file from the current working directory.\n * Falls back to defaults if no config file is found.\n */\nexport async function loadConfig(cwd?: string): Promise<SuitePortalConfig> {\n const dir = cwd ?? process.cwd();\n\n for (const name of CONFIG_NAMES) {\n const fullPath = resolve(dir, name);\n if (existsSync(fullPath)) {\n try {\n const fileUrl = pathToFileURL(fullPath).href;\n const mod = await import(fileUrl);\n return (mod.default ?? mod) as SuitePortalConfig;\n } catch {\n // If the .ts config can't be imported directly (no tsx/ts-node),\n // fall back to defaults\n return {};\n }\n }\n }\n\n return {};\n}\n\n/** Convert CLI config into IntrospectOptions. */\nexport function toIntrospectOptions(config: SuitePortalConfig): IntrospectOptions {\n return {\n recordTypes: config.recordTypes,\n outputDir: config.outputDir ?? '.suiteportal',\n fetchDetails: config.fetchDetails ?? true,\n includeCustomRecords: config.includeCustomRecords ?? true,\n };\n}\n"],"mappings":";;;AAAA,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,qBAAqB;AAG9B,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF;AAaA,eAAsB,WAAW,KAA0C;AACzE,QAAM,MAAM,OAAO,QAAQ,IAAI;AAE/B,aAAW,QAAQ,cAAc;AAC/B,UAAM,WAAW,QAAQ,KAAK,IAAI;AAClC,QAAI,WAAW,QAAQ,GAAG;AACxB,UAAI;AACF,cAAM,UAAU,cAAc,QAAQ,EAAE;AACxC,cAAM,MAAM,MAAM,OAAO;AACzB,eAAQ,IAAI,WAAW;AAAA,MACzB,QAAQ;AAGN,eAAO,CAAC;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,SAAO,CAAC;AACV;AAGO,SAAS,oBAAoB,QAA8C;AAChF,SAAO;AAAA,IACL,aAAa,OAAO;AAAA,IACpB,WAAW,OAAO,aAAa;AAAA,IAC/B,cAAc,OAAO,gBAAgB;AAAA,IACrC,sBAAsB,OAAO,wBAAwB;AAAA,EACvD;AACF;","names":[]}
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadConfig
4
+ } from "./chunk-ADG2JPAJ.js";
5
+ import {
6
+ logger
7
+ } from "./chunk-276NVAI2.js";
8
+
9
+ // src/commands/generate.ts
10
+ import { generate } from "@suiteportal/generator";
11
+ async function runGenerate() {
12
+ logger.info("Generating typed client...\n");
13
+ const config = await loadConfig();
14
+ const outputDir = config.outputDir ?? ".suiteportal";
15
+ try {
16
+ const result = await generate({
17
+ schemaPath: `${outputDir}/schema.json`,
18
+ outputDir: `${outputDir}/client`
19
+ });
20
+ console.log("");
21
+ logger.success("Code generation complete!\n");
22
+ logger.info("Summary:");
23
+ logger.dim(` Records: ${result.recordCount}`);
24
+ logger.dim(` Fields: ${result.fieldCount}`);
25
+ console.log("");
26
+ logger.info("Output files:");
27
+ for (const file of result.files) {
28
+ logger.dim(` ${file}`);
29
+ }
30
+ console.log("");
31
+ } catch (error) {
32
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") {
33
+ logger.error("Schema not found. Run `npx suiteportal introspect` first.");
34
+ } else {
35
+ logger.error(error instanceof Error ? error.message : String(error));
36
+ }
37
+ process.exit(1);
38
+ }
39
+ }
40
+ export {
41
+ runGenerate
42
+ };
43
+ //# sourceMappingURL=generate-4LSPAZPY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/generate.ts"],"sourcesContent":["import { generate } from '@suiteportal/generator';\nimport { logger } from '../utils/logger.js';\nimport { loadConfig } from '../utils/config-loader.js';\n\nexport async function runGenerate(): Promise<void> {\n logger.info('Generating typed client...\\n');\n\n const config = await loadConfig();\n const outputDir = config.outputDir ?? '.suiteportal';\n\n try {\n const result = await generate({\n schemaPath: `${outputDir}/schema.json`,\n outputDir: `${outputDir}/client`,\n });\n\n console.log('');\n logger.success('Code generation complete!\\n');\n logger.info('Summary:');\n logger.dim(` Records: ${result.recordCount}`);\n logger.dim(` Fields: ${result.fieldCount}`);\n console.log('');\n logger.info('Output files:');\n for (const file of result.files) {\n logger.dim(` ${file}`);\n }\n console.log('');\n } catch (error) {\n if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT') {\n logger.error('Schema not found. Run `npx suiteportal introspect` first.');\n } else {\n logger.error(error instanceof Error ? error.message : String(error));\n }\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,gBAAgB;AAIzB,eAAsB,cAA6B;AACjD,SAAO,KAAK,8BAA8B;AAE1C,QAAM,SAAS,MAAM,WAAW;AAChC,QAAM,YAAY,OAAO,aAAa;AAEtC,MAAI;AACF,UAAM,SAAS,MAAM,SAAS;AAAA,MAC5B,YAAY,GAAG,SAAS;AAAA,MACxB,WAAW,GAAG,SAAS;AAAA,IACzB,CAAC;AAED,YAAQ,IAAI,EAAE;AACd,WAAO,QAAQ,6BAA6B;AAC5C,WAAO,KAAK,UAAU;AACtB,WAAO,IAAI,cAAc,OAAO,WAAW,EAAE;AAC7C,WAAO,IAAI,cAAc,OAAO,UAAU,EAAE;AAC5C,YAAQ,IAAI,EAAE;AACd,WAAO,KAAK,eAAe;AAC3B,eAAW,QAAQ,OAAO,OAAO;AAC/B,aAAO,IAAI,KAAK,IAAI,EAAE;AAAA,IACxB;AACA,YAAQ,IAAI,EAAE;AAAA,EAChB,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,UAAU,SAAU,MAAgC,SAAS,UAAU;AACnG,aAAO,MAAM,2DAA2D;AAAA,IAC1E,OAAO;AACL,aAAO,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACrE;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger
4
+ } from "./chunk-276NVAI2.js";
5
+
6
+ // src/index.ts
7
+ var HELP_TEXT = `
8
+ Usage: suiteportal <command>
9
+
10
+ Commands:
11
+ init Create config and .env template files
12
+ introspect Pull schema from your NetSuite account
13
+ generate Generate typed client (coming soon)
14
+
15
+ Options:
16
+ --help, -h Show this help message
17
+ --version Show version
18
+ `;
19
+ async function main() {
20
+ const command = process.argv[2];
21
+ switch (command) {
22
+ case "init": {
23
+ const { runInit } = await import("./init-EGGAZGWX.js");
24
+ await runInit();
25
+ break;
26
+ }
27
+ case "introspect": {
28
+ const { runIntrospect } = await import("./introspect-AOGF5S3E.js");
29
+ await runIntrospect();
30
+ break;
31
+ }
32
+ case "generate": {
33
+ const { runGenerate } = await import("./generate-4LSPAZPY.js");
34
+ await runGenerate();
35
+ break;
36
+ }
37
+ case "--help":
38
+ case "-h":
39
+ case void 0: {
40
+ logger.banner();
41
+ console.log(HELP_TEXT);
42
+ break;
43
+ }
44
+ case "--version": {
45
+ console.log("0.1.0");
46
+ break;
47
+ }
48
+ default: {
49
+ logger.error(`Unknown command: ${command}`);
50
+ console.log(HELP_TEXT);
51
+ process.exit(1);
52
+ }
53
+ }
54
+ }
55
+ main().catch((error) => {
56
+ logger.error(error instanceof Error ? error.message : String(error));
57
+ process.exit(1);
58
+ });
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import { logger } from './utils/logger.js';\n\nconst HELP_TEXT = `\nUsage: suiteportal <command>\n\nCommands:\n init Create config and .env template files\n introspect Pull schema from your NetSuite account\n generate Generate typed client (coming soon)\n\nOptions:\n --help, -h Show this help message\n --version Show version\n`;\n\nasync function main(): Promise<void> {\n const command = process.argv[2];\n\n switch (command) {\n case 'init': {\n const { runInit } = await import('./commands/init.js');\n await runInit();\n break;\n }\n\n case 'introspect': {\n const { runIntrospect } = await import('./commands/introspect.js');\n await runIntrospect();\n break;\n }\n\n case 'generate': {\n const { runGenerate } = await import('./commands/generate.js');\n await runGenerate();\n break;\n }\n\n case '--help':\n case '-h':\n case undefined: {\n logger.banner();\n console.log(HELP_TEXT);\n break;\n }\n\n case '--version': {\n console.log('0.1.0');\n break;\n }\n\n default: {\n logger.error(`Unknown command: ${command}`);\n console.log(HELP_TEXT);\n process.exit(1);\n }\n }\n}\n\nmain().catch((error) => {\n logger.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n});\n"],"mappings":";;;;;;AAEA,IAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAalB,eAAe,OAAsB;AACnC,QAAM,UAAU,QAAQ,KAAK,CAAC;AAE9B,UAAQ,SAAS;AAAA,IACf,KAAK,QAAQ;AACX,YAAM,EAAE,QAAQ,IAAI,MAAM,OAAO,oBAAoB;AACrD,YAAM,QAAQ;AACd;AAAA,IACF;AAAA,IAEA,KAAK,cAAc;AACjB,YAAM,EAAE,cAAc,IAAI,MAAM,OAAO,0BAA0B;AACjE,YAAM,cAAc;AACpB;AAAA,IACF;AAAA,IAEA,KAAK,YAAY;AACf,YAAM,EAAE,YAAY,IAAI,MAAM,OAAO,wBAAwB;AAC7D,YAAM,YAAY;AAClB;AAAA,IACF;AAAA,IAEA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK,QAAW;AACd,aAAO,OAAO;AACd,cAAQ,IAAI,SAAS;AACrB;AAAA,IACF;AAAA,IAEA,KAAK,aAAa;AAChB,cAAQ,IAAI,OAAO;AACnB;AAAA,IACF;AAAA,IAEA,SAAS;AACP,aAAO,MAAM,oBAAoB,OAAO,EAAE;AAC1C,cAAQ,IAAI,SAAS;AACrB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,SAAO,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACnE,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ logger
4
+ } from "./chunk-276NVAI2.js";
5
+
6
+ // src/commands/init.ts
7
+ import { writeFile, access } from "fs/promises";
8
+ import { resolve } from "path";
9
+
10
+ // src/utils/templates.ts
11
+ var CONFIG_TEMPLATE = `import type { NetSuiteConfig } from '@suiteportal/connector';
12
+
13
+ /**
14
+ * SuitePortal configuration.
15
+ * Credentials are loaded from environment variables.
16
+ */
17
+ export default {
18
+ // Record types to introspect. Omit or leave empty for all.
19
+ // recordTypes: ['salesorder', 'customer', 'invoice'],
20
+
21
+ // Output directory for schema files.
22
+ outputDir: '.suiteportal',
23
+
24
+ // Fetch full metadata for each record type (slower but richer).
25
+ fetchDetails: true,
26
+
27
+ // Include custom records discovered via SuiteQL.
28
+ includeCustomRecords: true,
29
+ } satisfies {
30
+ recordTypes?: string[];
31
+ outputDir?: string;
32
+ fetchDetails?: boolean;
33
+ includeCustomRecords?: boolean;
34
+ };
35
+ `;
36
+ var ENV_TEMPLATE = `# NetSuite OAuth 1.0a Credentials
37
+ # Get these from Setup > Integration > Manage Integrations (consumer key/secret)
38
+ # and Setup > Users/Roles > Access Tokens (token ID/secret)
39
+
40
+ NETSUITE_ACCOUNT_ID=
41
+ NETSUITE_CONSUMER_KEY=
42
+ NETSUITE_CONSUMER_SECRET=
43
+ NETSUITE_TOKEN_ID=
44
+ NETSUITE_TOKEN_SECRET=
45
+ `;
46
+ var CONFIG_FILENAME = "suiteportal.config.ts";
47
+ var ENV_FILENAME = ".env";
48
+
49
+ // src/commands/init.ts
50
+ async function runInit() {
51
+ logger.info("Initializing SuitePortal project...");
52
+ const cwd = process.cwd();
53
+ const configPath = resolve(cwd, CONFIG_FILENAME);
54
+ const envPath = resolve(cwd, ENV_FILENAME);
55
+ if (await fileExists(configPath)) {
56
+ logger.warn(`${CONFIG_FILENAME} already exists, skipping`);
57
+ } else {
58
+ await writeFile(configPath, CONFIG_TEMPLATE, "utf-8");
59
+ logger.success(`Created ${CONFIG_FILENAME}`);
60
+ }
61
+ if (await fileExists(envPath)) {
62
+ logger.warn(`${ENV_FILENAME} already exists, skipping`);
63
+ } else {
64
+ await writeFile(envPath, ENV_TEMPLATE, "utf-8");
65
+ logger.success(`Created ${ENV_FILENAME}`);
66
+ }
67
+ console.log("");
68
+ logger.info("Next steps:");
69
+ logger.step(`Fill in your NetSuite credentials in ${ENV_FILENAME}`);
70
+ logger.step("Run `npx suiteportal introspect` to pull your schema");
71
+ console.log("");
72
+ }
73
+ async function fileExists(path) {
74
+ try {
75
+ await access(path);
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+ export {
82
+ runInit
83
+ };
84
+ //# sourceMappingURL=init-EGGAZGWX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/init.ts","../src/utils/templates.ts"],"sourcesContent":["import { writeFile, access } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { logger } from '../utils/logger.js';\nimport { CONFIG_TEMPLATE, ENV_TEMPLATE, CONFIG_FILENAME, ENV_FILENAME } from '../utils/templates.js';\n\nexport async function runInit(): Promise<void> {\n logger.info('Initializing SuitePortal project...');\n\n const cwd = process.cwd();\n const configPath = resolve(cwd, CONFIG_FILENAME);\n const envPath = resolve(cwd, ENV_FILENAME);\n\n // Write config file (skip if exists)\n if (await fileExists(configPath)) {\n logger.warn(`${CONFIG_FILENAME} already exists, skipping`);\n } else {\n await writeFile(configPath, CONFIG_TEMPLATE, 'utf-8');\n logger.success(`Created ${CONFIG_FILENAME}`);\n }\n\n // Write .env file (skip if exists)\n if (await fileExists(envPath)) {\n logger.warn(`${ENV_FILENAME} already exists, skipping`);\n } else {\n await writeFile(envPath, ENV_TEMPLATE, 'utf-8');\n logger.success(`Created ${ENV_FILENAME}`);\n }\n\n console.log('');\n logger.info('Next steps:');\n logger.step(`Fill in your NetSuite credentials in ${ENV_FILENAME}`);\n logger.step('Run `npx suiteportal introspect` to pull your schema');\n console.log('');\n}\n\nasync function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n","export const CONFIG_TEMPLATE = `import type { NetSuiteConfig } from '@suiteportal/connector';\n\n/**\n * SuitePortal configuration.\n * Credentials are loaded from environment variables.\n */\nexport default {\n // Record types to introspect. Omit or leave empty for all.\n // recordTypes: ['salesorder', 'customer', 'invoice'],\n\n // Output directory for schema files.\n outputDir: '.suiteportal',\n\n // Fetch full metadata for each record type (slower but richer).\n fetchDetails: true,\n\n // Include custom records discovered via SuiteQL.\n includeCustomRecords: true,\n} satisfies {\n recordTypes?: string[];\n outputDir?: string;\n fetchDetails?: boolean;\n includeCustomRecords?: boolean;\n};\n`;\n\nexport const ENV_TEMPLATE = `# NetSuite OAuth 1.0a Credentials\n# Get these from Setup > Integration > Manage Integrations (consumer key/secret)\n# and Setup > Users/Roles > Access Tokens (token ID/secret)\n\nNETSUITE_ACCOUNT_ID=\nNETSUITE_CONSUMER_KEY=\nNETSUITE_CONSUMER_SECRET=\nNETSUITE_TOKEN_ID=\nNETSUITE_TOKEN_SECRET=\n`;\n\nexport const CONFIG_FILENAME = 'suiteportal.config.ts';\nexport const ENV_FILENAME = '.env';\n"],"mappings":";;;;;;AAAA,SAAS,WAAW,cAAc;AAClC,SAAS,eAAe;;;ACDjB,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BxB,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWrB,IAAM,kBAAkB;AACxB,IAAM,eAAe;;;ADjC5B,eAAsB,UAAyB;AAC7C,SAAO,KAAK,qCAAqC;AAEjD,QAAM,MAAM,QAAQ,IAAI;AACxB,QAAM,aAAa,QAAQ,KAAK,eAAe;AAC/C,QAAM,UAAU,QAAQ,KAAK,YAAY;AAGzC,MAAI,MAAM,WAAW,UAAU,GAAG;AAChC,WAAO,KAAK,GAAG,eAAe,2BAA2B;AAAA,EAC3D,OAAO;AACL,UAAM,UAAU,YAAY,iBAAiB,OAAO;AACpD,WAAO,QAAQ,WAAW,eAAe,EAAE;AAAA,EAC7C;AAGA,MAAI,MAAM,WAAW,OAAO,GAAG;AAC7B,WAAO,KAAK,GAAG,YAAY,2BAA2B;AAAA,EACxD,OAAO;AACL,UAAM,UAAU,SAAS,cAAc,OAAO;AAC9C,WAAO,QAAQ,WAAW,YAAY,EAAE;AAAA,EAC1C;AAEA,UAAQ,IAAI,EAAE;AACd,SAAO,KAAK,aAAa;AACzB,SAAO,KAAK,wCAAwC,YAAY,EAAE;AAClE,SAAO,KAAK,sDAAsD;AAClE,UAAQ,IAAI,EAAE;AAChB;AAEA,eAAe,WAAW,MAAgC;AACxD,MAAI;AACF,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ loadConfig,
4
+ toIntrospectOptions
5
+ } from "./chunk-ADG2JPAJ.js";
6
+ import {
7
+ logger
8
+ } from "./chunk-276NVAI2.js";
9
+
10
+ // src/commands/introspect.ts
11
+ import { NetSuiteClient } from "@suiteportal/connector";
12
+ import { introspect } from "@suiteportal/introspector";
13
+
14
+ // src/utils/env.ts
15
+ import { config as loadDotenv } from "dotenv";
16
+ var REQUIRED_VARS = [
17
+ "NETSUITE_ACCOUNT_ID",
18
+ "NETSUITE_CONSUMER_KEY",
19
+ "NETSUITE_CONSUMER_SECRET",
20
+ "NETSUITE_TOKEN_ID",
21
+ "NETSUITE_TOKEN_SECRET"
22
+ ];
23
+ function loadEnv() {
24
+ loadDotenv();
25
+ }
26
+ function buildNetSuiteConfig() {
27
+ const missing = [];
28
+ for (const varName of REQUIRED_VARS) {
29
+ if (!process.env[varName]) {
30
+ missing.push(varName);
31
+ }
32
+ }
33
+ if (missing.length > 0) {
34
+ throw new Error(
35
+ `Missing required environment variables:
36
+ ${missing.map((v) => ` - ${v}`).join("\n")}
37
+
38
+ Set them in .env or your shell environment.`
39
+ );
40
+ }
41
+ return {
42
+ accountId: process.env["NETSUITE_ACCOUNT_ID"],
43
+ consumerKey: process.env["NETSUITE_CONSUMER_KEY"],
44
+ consumerSecret: process.env["NETSUITE_CONSUMER_SECRET"],
45
+ tokenId: process.env["NETSUITE_TOKEN_ID"],
46
+ tokenSecret: process.env["NETSUITE_TOKEN_SECRET"],
47
+ timeout: process.env["NETSUITE_TIMEOUT_MS"] ? parseInt(process.env["NETSUITE_TIMEOUT_MS"], 10) : void 0
48
+ };
49
+ }
50
+
51
+ // src/commands/introspect.ts
52
+ async function runIntrospect() {
53
+ logger.info("Starting introspection...\n");
54
+ loadEnv();
55
+ let nsConfig;
56
+ try {
57
+ nsConfig = buildNetSuiteConfig();
58
+ } catch (error) {
59
+ logger.error(error instanceof Error ? error.message : String(error));
60
+ process.exit(1);
61
+ }
62
+ const projectConfig = await loadConfig();
63
+ const options = toIntrospectOptions(projectConfig);
64
+ const client = new NetSuiteClient(nsConfig);
65
+ logger.dim(` Account: ${nsConfig.accountId}`);
66
+ logger.dim(` Base URL: ${client.baseUrl}
67
+ `);
68
+ try {
69
+ const result = await introspect(
70
+ client,
71
+ nsConfig.accountId,
72
+ options,
73
+ (message) => logger.step(message)
74
+ );
75
+ console.log("");
76
+ logger.success("Introspection complete!\n");
77
+ logger.info("Summary:");
78
+ logger.dim(` Records: ${result.stats.totalRecords}`);
79
+ logger.dim(` Fields: ${result.stats.totalFields}`);
80
+ logger.dim(` Relations: ${result.stats.totalRelations}`);
81
+ logger.dim(` Custom types: ${result.stats.customRecordTypes}`);
82
+ logger.dim(` Custom fields: ${result.stats.customFields}`);
83
+ console.log("");
84
+ logger.info("Output files:");
85
+ logger.dim(` ${result.files.schemaPath}`);
86
+ logger.dim(` ${result.files.relationsPath}`);
87
+ logger.dim(` ${result.files.rawPath}`);
88
+ console.log("");
89
+ } catch (error) {
90
+ logger.error(error instanceof Error ? error.message : String(error));
91
+ process.exit(1);
92
+ }
93
+ }
94
+ export {
95
+ runIntrospect
96
+ };
97
+ //# sourceMappingURL=introspect-AOGF5S3E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/introspect.ts","../src/utils/env.ts"],"sourcesContent":["import { NetSuiteClient } from '@suiteportal/connector';\nimport { introspect } from '@suiteportal/introspector';\nimport { logger } from '../utils/logger.js';\nimport { loadEnv, buildNetSuiteConfig } from '../utils/env.js';\nimport { loadConfig, toIntrospectOptions } from '../utils/config-loader.js';\n\nexport async function runIntrospect(): Promise<void> {\n logger.info('Starting introspection...\\n');\n\n // Load environment\n loadEnv();\n\n // Build config\n let nsConfig;\n try {\n nsConfig = buildNetSuiteConfig();\n } catch (error) {\n logger.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n\n // Load project config\n const projectConfig = await loadConfig();\n const options = toIntrospectOptions(projectConfig);\n\n // Create client\n const client = new NetSuiteClient(nsConfig);\n logger.dim(` Account: ${nsConfig.accountId}`);\n logger.dim(` Base URL: ${client.baseUrl}\\n`);\n\n // Run introspection\n try {\n const result = await introspect(\n client,\n nsConfig.accountId,\n options,\n (message) => logger.step(message),\n );\n\n console.log('');\n logger.success('Introspection complete!\\n');\n logger.info('Summary:');\n logger.dim(` Records: ${result.stats.totalRecords}`);\n logger.dim(` Fields: ${result.stats.totalFields}`);\n logger.dim(` Relations: ${result.stats.totalRelations}`);\n logger.dim(` Custom types: ${result.stats.customRecordTypes}`);\n logger.dim(` Custom fields: ${result.stats.customFields}`);\n console.log('');\n logger.info('Output files:');\n logger.dim(` ${result.files.schemaPath}`);\n logger.dim(` ${result.files.relationsPath}`);\n logger.dim(` ${result.files.rawPath}`);\n console.log('');\n } catch (error) {\n logger.error(error instanceof Error ? error.message : String(error));\n process.exit(1);\n }\n}\n","import { config as loadDotenv } from 'dotenv';\nimport type { NetSuiteConfig } from '@suiteportal/connector';\n\nconst REQUIRED_VARS = [\n 'NETSUITE_ACCOUNT_ID',\n 'NETSUITE_CONSUMER_KEY',\n 'NETSUITE_CONSUMER_SECRET',\n 'NETSUITE_TOKEN_ID',\n 'NETSUITE_TOKEN_SECRET',\n] as const;\n\n/** Load .env and validate that all required NetSuite variables are set. */\nexport function loadEnv(): void {\n loadDotenv();\n}\n\n/** Build a NetSuiteConfig from environment variables. Throws if any are missing. */\nexport function buildNetSuiteConfig(): NetSuiteConfig {\n const missing: string[] = [];\n\n for (const varName of REQUIRED_VARS) {\n if (!process.env[varName]) {\n missing.push(varName);\n }\n }\n\n if (missing.length > 0) {\n throw new Error(\n `Missing required environment variables:\\n${missing.map((v) => ` - ${v}`).join('\\n')}\\n\\nSet them in .env or your shell environment.`,\n );\n }\n\n return {\n accountId: process.env['NETSUITE_ACCOUNT_ID']!,\n consumerKey: process.env['NETSUITE_CONSUMER_KEY']!,\n consumerSecret: process.env['NETSUITE_CONSUMER_SECRET']!,\n tokenId: process.env['NETSUITE_TOKEN_ID']!,\n tokenSecret: process.env['NETSUITE_TOKEN_SECRET']!,\n timeout: process.env['NETSUITE_TIMEOUT_MS']\n ? parseInt(process.env['NETSUITE_TIMEOUT_MS'], 10)\n : undefined,\n };\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;;;ACD3B,SAAS,UAAU,kBAAkB;AAGrC,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAGO,SAAS,UAAgB;AAC9B,aAAW;AACb;AAGO,SAAS,sBAAsC;AACpD,QAAM,UAAoB,CAAC;AAE3B,aAAW,WAAW,eAAe;AACnC,QAAI,CAAC,QAAQ,IAAI,OAAO,GAAG;AACzB,cAAQ,KAAK,OAAO;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,IAAI;AAAA,MACR;AAAA,EAA4C,QAAQ,IAAI,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA;AAAA,IACvF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,WAAW,QAAQ,IAAI,qBAAqB;AAAA,IAC5C,aAAa,QAAQ,IAAI,uBAAuB;AAAA,IAChD,gBAAgB,QAAQ,IAAI,0BAA0B;AAAA,IACtD,SAAS,QAAQ,IAAI,mBAAmB;AAAA,IACxC,aAAa,QAAQ,IAAI,uBAAuB;AAAA,IAChD,SAAS,QAAQ,IAAI,qBAAqB,IACtC,SAAS,QAAQ,IAAI,qBAAqB,GAAG,EAAE,IAC/C;AAAA,EACN;AACF;;;ADpCA,eAAsB,gBAA+B;AACnD,SAAO,KAAK,6BAA6B;AAGzC,UAAQ;AAGR,MAAI;AACJ,MAAI;AACF,eAAW,oBAAoB;AAAA,EACjC,SAAS,OAAO;AACd,WAAO,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,QAAM,gBAAgB,MAAM,WAAW;AACvC,QAAM,UAAU,oBAAoB,aAAa;AAGjD,QAAM,SAAS,IAAI,eAAe,QAAQ;AAC1C,SAAO,IAAI,cAAc,SAAS,SAAS,EAAE;AAC7C,SAAO,IAAI,eAAe,OAAO,OAAO;AAAA,CAAI;AAG5C,MAAI;AACF,UAAM,SAAS,MAAM;AAAA,MACnB;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA,CAAC,YAAY,OAAO,KAAK,OAAO;AAAA,IAClC;AAEA,YAAQ,IAAI,EAAE;AACd,WAAO,QAAQ,2BAA2B;AAC1C,WAAO,KAAK,UAAU;AACtB,WAAO,IAAI,oBAAoB,OAAO,MAAM,YAAY,EAAE;AAC1D,WAAO,IAAI,oBAAoB,OAAO,MAAM,WAAW,EAAE;AACzD,WAAO,IAAI,oBAAoB,OAAO,MAAM,cAAc,EAAE;AAC5D,WAAO,IAAI,oBAAoB,OAAO,MAAM,iBAAiB,EAAE;AAC/D,WAAO,IAAI,oBAAoB,OAAO,MAAM,YAAY,EAAE;AAC1D,YAAQ,IAAI,EAAE;AACd,WAAO,KAAK,eAAe;AAC3B,WAAO,IAAI,KAAK,OAAO,MAAM,UAAU,EAAE;AACzC,WAAO,IAAI,KAAK,OAAO,MAAM,aAAa,EAAE;AAC5C,WAAO,IAAI,KAAK,OAAO,MAAM,OAAO,EAAE;AACtC,YAAQ,IAAI,EAAE;AAAA,EAChB,SAAS,OAAO;AACd,WAAO,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACnE,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@suiteportal/cli",
3
+ "version": "0.1.0",
4
+ "description": "CLI for SuitePortal NetSuite ORM — introspect, generate, and manage",
5
+ "type": "module",
6
+ "bin": {
7
+ "suiteportal": "./dist/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "files": ["dist"],
12
+ "scripts": {
13
+ "build": "tsup",
14
+ "clean": "rm -rf dist"
15
+ },
16
+ "dependencies": {
17
+ "@suiteportal/connector": "^0.1.0",
18
+ "@suiteportal/introspector": "^0.1.0",
19
+ "@suiteportal/generator": "^0.1.0",
20
+ "dotenv": "^16.4.0"
21
+ },
22
+ "license": "MIT",
23
+ "author": "Trey Hulse",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/treyhulse/netsuite-orm",
27
+ "directory": "packages/cli"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "engines": {
33
+ "node": ">=20.0.0"
34
+ },
35
+ "keywords": ["netsuite", "cli", "suiteportal", "orm", "codegen"]
36
+ }