mindsim 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.
Files changed (79) hide show
  1. package/.github/workflows/publish.yml +32 -0
  2. package/.github/workflows/test.yml +28 -0
  3. package/.github/workflows/type-checks.yml +29 -0
  4. package/LICENSE +21 -0
  5. package/README.md +748 -0
  6. package/assets/mindsim-logo.svg +15 -0
  7. package/biome.jsonc +43 -0
  8. package/dist/auth.d.ts +5 -0
  9. package/dist/auth.d.ts.map +1 -0
  10. package/dist/auth.js +115 -0
  11. package/dist/auth.js.map +1 -0
  12. package/dist/cli.d.ts +3 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +36 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/config.d.ts +8 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +63 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/index.d.ts +20 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +71 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/resources/artifacts.d.ts +8 -0
  25. package/dist/resources/artifacts.d.ts.map +1 -0
  26. package/dist/resources/artifacts.js +17 -0
  27. package/dist/resources/artifacts.js.map +1 -0
  28. package/dist/resources/minds.d.ts +39 -0
  29. package/dist/resources/minds.d.ts.map +1 -0
  30. package/dist/resources/minds.js +85 -0
  31. package/dist/resources/minds.js.map +1 -0
  32. package/dist/resources/psychometrics.d.ts +11 -0
  33. package/dist/resources/psychometrics.d.ts.map +1 -0
  34. package/dist/resources/psychometrics.js +18 -0
  35. package/dist/resources/psychometrics.js.map +1 -0
  36. package/dist/resources/simulations.d.ts +26 -0
  37. package/dist/resources/simulations.d.ts.map +1 -0
  38. package/dist/resources/simulations.js +41 -0
  39. package/dist/resources/simulations.js.map +1 -0
  40. package/dist/resources/snapshots.d.ts +27 -0
  41. package/dist/resources/snapshots.d.ts.map +1 -0
  42. package/dist/resources/snapshots.js +101 -0
  43. package/dist/resources/snapshots.js.map +1 -0
  44. package/dist/resources/tags.d.ts +23 -0
  45. package/dist/resources/tags.d.ts.map +1 -0
  46. package/dist/resources/tags.js +32 -0
  47. package/dist/resources/tags.js.map +1 -0
  48. package/dist/types.d.ts +161 -0
  49. package/dist/types.d.ts.map +1 -0
  50. package/dist/types.js +3 -0
  51. package/dist/types.js.map +1 -0
  52. package/dist/version.d.ts +15 -0
  53. package/dist/version.d.ts.map +1 -0
  54. package/dist/version.js +105 -0
  55. package/dist/version.js.map +1 -0
  56. package/package.json +55 -0
  57. package/src/auth.ts +131 -0
  58. package/src/cli.ts +41 -0
  59. package/src/config.ts +60 -0
  60. package/src/index.ts +59 -0
  61. package/src/resources/artifacts.ts +13 -0
  62. package/src/resources/minds.ts +98 -0
  63. package/src/resources/psychometrics.ts +16 -0
  64. package/src/resources/simulations.ts +49 -0
  65. package/src/resources/snapshots.ts +126 -0
  66. package/src/resources/tags.ts +30 -0
  67. package/src/types.ts +185 -0
  68. package/src/version.ts +111 -0
  69. package/tests/auth.test.ts +41 -0
  70. package/tests/config.test.ts +129 -0
  71. package/tests/resources/minds.test.ts +119 -0
  72. package/tests/resources/psychometrics.test.ts +38 -0
  73. package/tests/resources/simulation.test.ts +94 -0
  74. package/tests/resources/snapshots.test.ts +135 -0
  75. package/tests/resources/tags.test.ts +87 -0
  76. package/tests/use-cases/quickstart.test.ts +84 -0
  77. package/tests/version.test.ts +221 -0
  78. package/tsconfig.json +29 -0
  79. package/vitest.config.ts +12 -0
package/src/types.ts ADDED
@@ -0,0 +1,185 @@
1
+ export interface AuthConfig {
2
+ authServerUrl: string;
3
+ authServerLandingPath: string;
4
+ authRedirectPath: string;
5
+ listenPort: string;
6
+ }
7
+
8
+ export interface AuthResponse {
9
+ apiKey?: {
10
+ id: string;
11
+ key: string;
12
+ };
13
+ }
14
+
15
+ export interface Tag {
16
+ id: string;
17
+ name: string;
18
+ organizationId?: string | null;
19
+ createdAt?: string;
20
+ updatedAt?: string;
21
+ mindCount?: number;
22
+ }
23
+
24
+ export interface Mind {
25
+ id: string;
26
+ name: string;
27
+ slug?: string | null;
28
+ email?: string | null;
29
+ imageUrl?: string | null;
30
+ socialMedia?: Record<string, any> | null;
31
+ scope: string;
32
+ isSelf: boolean;
33
+ organizationId?: string | null;
34
+ createdAt?: string;
35
+ updatedAt?: string;
36
+ tags: Tag[];
37
+ }
38
+
39
+ export interface CreateMindRequest {
40
+ name: string;
41
+ email?: string;
42
+ imageUrl?: string;
43
+ socialMedia?: Record<string, any>;
44
+ tags?: string[];
45
+ }
46
+
47
+ export interface UpdateMindRequest {
48
+ name?: string;
49
+ email?: string;
50
+ socialMedia?: Record<string, any>;
51
+ tags?: string[]; // SDK converts this array to a comma-separated string
52
+ }
53
+
54
+ export interface SearchMindsParams {
55
+ query: string;
56
+ }
57
+
58
+ export interface ListMindsParams {
59
+ tags?: string[]; // SDK converts this array to a comma-separated string
60
+ }
61
+
62
+ export interface SimulationScenario {
63
+ message: string;
64
+ }
65
+
66
+ export interface RunSimulationRequest {
67
+ mindId: string;
68
+ scenario: SimulationScenario;
69
+ runInBackground?: boolean;
70
+ }
71
+
72
+ export interface SimulationResponse {
73
+ message: string;
74
+ }
75
+
76
+ export interface SimulationMessage {
77
+ id: string;
78
+ role: "assistant" | "user" | "system";
79
+ content: { text: string } | any;
80
+ status: string;
81
+ createdAt?: string;
82
+ }
83
+
84
+ export interface Simulation {
85
+ id: string;
86
+ userId: string;
87
+ title: string;
88
+ mindId?: string | null;
89
+ createdAt?: string;
90
+ updatedAt?: string;
91
+ }
92
+
93
+ export interface GetSimulationResponse {
94
+ simulation: Simulation;
95
+ messages: SimulationMessage[];
96
+ }
97
+
98
+ export interface ListSimulationsParams {
99
+ offset?: number;
100
+ limit?: number;
101
+ }
102
+
103
+ export interface ListSimulationsResponse {
104
+ simulations: Simulation[];
105
+ count: number;
106
+ }
107
+
108
+ export interface GetSignedUrlParams {
109
+ fileName?: string;
110
+ contentType?: string;
111
+ }
112
+
113
+ export interface GetSignedUrlResponse {
114
+ signedUrl: string;
115
+ fileName: string;
116
+ artifactId: string;
117
+ contentType: string;
118
+ }
119
+
120
+ export interface CreateSnapshotParams {
121
+ file: Buffer | any; // Buffer (Node) or Blob/File (Browser)
122
+ fileName: string;
123
+ contentType: string;
124
+ mindsetDate?: string;
125
+ }
126
+
127
+ export interface CreateSnapshotFromFileParams {
128
+ file?: Buffer | any; // Buffer for Node, File for Browser
129
+ fileName?: string; // Required if passing a Buffer
130
+ artifactId?: string;
131
+ mindsetDate?: string;
132
+ }
133
+
134
+ export interface CreateSnapshotResponse {
135
+ message: string;
136
+ mindAssessmentId: string;
137
+ artifactId: string;
138
+ }
139
+
140
+ export interface SnapshotStatus {
141
+ id: string;
142
+ mindId: string;
143
+ status: string;
144
+ startedAt?: string;
145
+ completedAt?: string;
146
+ }
147
+
148
+ export interface MindAssessment {
149
+ id: string;
150
+ mindId: string;
151
+ artifactId?: string | null;
152
+ mindsetDate?: string | null;
153
+ status: string;
154
+ startedAt?: string | null;
155
+ completedAt?: string | null;
156
+ createdAt?: string | null;
157
+ updatedAt?: string | null;
158
+ psychometricCount?: number | null;
159
+ keyTopicCount?: number | null;
160
+ findingCount?: number | null;
161
+ }
162
+
163
+ export interface ListSnapshotsResponse {
164
+ snapshots: MindAssessment[];
165
+ count: number;
166
+ }
167
+
168
+ // -- Psychometrics --
169
+
170
+ export interface PersonDetails {
171
+ id: string;
172
+ slug?: string | null;
173
+ name: string;
174
+ imageUrl?: string | null;
175
+ mindDate: string;
176
+ mindAssesssmentId: string;
177
+ }
178
+
179
+ export interface GetPsychometricsResponse {
180
+ personDetails: PersonDetails;
181
+ dimensionAnalysis?: Record<string, any> | null;
182
+ metaAnalysis?: Record<string, any> | null;
183
+ topicsSentimentSummary?: Record<string, any> | null;
184
+ mindReasonerStopError?: any | null;
185
+ }
package/src/version.ts ADDED
@@ -0,0 +1,111 @@
1
+ import { exec } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { promisify } from "node:util";
5
+ import axios from "axios";
6
+ import semver from "semver";
7
+
8
+ const execAsync = promisify(exec);
9
+
10
+ const PACKAGE_NAME = "@mindsim/mindsim-sdk-typescript";
11
+ const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}`;
12
+
13
+ /**
14
+ * REUSABLE VERSION EXTRACTOR
15
+ * Reads the version directly from the SDK's package.json
16
+ */
17
+ export const getPackageVersion = (): string => {
18
+ try {
19
+ // Determine path based on whether we are in ./src or ./dist
20
+ // __dirname is usually ./dist in production
21
+ const packagePath = path.resolve(__dirname, "../package.json");
22
+
23
+ // Fallback if structure differs slightly in dev vs prod
24
+ if (!fs.existsSync(packagePath)) {
25
+ const altPath = path.resolve(__dirname, "../../package.json");
26
+ if (fs.existsSync(altPath)) {
27
+ return JSON.parse(fs.readFileSync(altPath, "utf-8")).version;
28
+ }
29
+ }
30
+
31
+ const content = fs.readFileSync(packagePath, "utf-8");
32
+ const pkg = JSON.parse(content);
33
+ return pkg.version;
34
+ } catch (error) {
35
+ console.warn("MindSim SDK: Unable to determine current package version.", error);
36
+ return "0.0.0";
37
+ }
38
+ };
39
+
40
+ /**
41
+ * Checks NPM registry for updates and logs a warning if outdated.
42
+ * @param verbose If true, logs when the version is up to date (useful for CLI 'version' command)
43
+ */
44
+ export const checkForUpdates = async (verbose = false): Promise<void> => {
45
+ try {
46
+ const currentVersion = getPackageVersion();
47
+
48
+ // Fetch latest version data from NPM
49
+ // Set a short timeout so we don't block application boot significantly
50
+ const { data } = await axios.get(REGISTRY_URL, { timeout: 1500 });
51
+ const latestVersion = data["dist-tags"].latest;
52
+
53
+ if (semver.gt(latestVersion, currentVersion)) {
54
+ const changelogUrl = `https://www.npmjs.com/package/${PACKAGE_NAME}/v/${latestVersion}`;
55
+
56
+ console.warn("\n" + "=".repeat(60));
57
+ console.warn(`⚠️ UPDATE AVAILABLE: @mindsim/mindsim-sdk-typescript`);
58
+ console.warn("=".repeat(60));
59
+ console.warn(` Current Version: ${currentVersion}`);
60
+ console.warn(` Latest Version: ${latestVersion}`);
61
+ console.warn("-".repeat(60));
62
+ console.warn(` Changelog: ${changelogUrl}`);
63
+ console.warn(` Run 'mindsim update' to install the latest version.`);
64
+ console.warn("=".repeat(60) + "\n");
65
+ } else if (verbose) {
66
+ console.log(`MindSim SDK is up to date (v${currentVersion})`);
67
+ }
68
+ } catch (error) {
69
+ // Fail silently on network errors so we don't break the user's workflow
70
+ if (verbose) {
71
+ console.error(
72
+ "Failed to check for updates:",
73
+ error instanceof Error ? error.message : "Unknown error",
74
+ );
75
+ }
76
+ }
77
+ };
78
+
79
+ /**
80
+ * Auto-updates the SDK using npm
81
+ */
82
+ export const updateSdk = async (): Promise<void> => {
83
+ console.log(`Checking for updates for ${PACKAGE_NAME}...`);
84
+
85
+ try {
86
+ const currentVersion = getPackageVersion();
87
+ const { data } = await axios.get(REGISTRY_URL);
88
+ const latestVersion = data["dist-tags"].latest;
89
+
90
+ if (!semver.gt(latestVersion, currentVersion)) {
91
+ console.log(`You are already on the latest version (${currentVersion}).`);
92
+ return;
93
+ }
94
+
95
+ console.log(`Updating from ${currentVersion} to ${latestVersion}...`);
96
+ console.log(`Running: npm install ${PACKAGE_NAME}@latest --save`);
97
+
98
+ // We use npm install --save to ensure it updates package.json
99
+ await execAsync(`npm install ${PACKAGE_NAME}@latest --save`);
100
+
101
+ console.log("✅ Update complete! Please restart your application.");
102
+ } catch (error) {
103
+ console.error("❌ Failed to update MindSim SDK.");
104
+ if (error instanceof Error) {
105
+ console.error(error.message);
106
+ }
107
+ console.error(
108
+ "Please try running manually: npm install @mindsim/mindsim-sdk-typescript@latest",
109
+ );
110
+ }
111
+ };
@@ -0,0 +1,41 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import * as configModule from "../src/config";
3
+ import { MindSim } from "../src/index";
4
+
5
+ // Mock the config loader specifically
6
+ vi.mock("../src/config", async (importOriginal) => {
7
+ const actual = await importOriginal<typeof configModule>();
8
+ return {
9
+ ...actual,
10
+ loadApiKey: vi.fn(),
11
+ };
12
+ });
13
+
14
+ describe("MindSim Authentication", () => {
15
+ beforeEach(() => {
16
+ vi.resetModules();
17
+ vi.mocked(configModule.loadApiKey).mockReturnValue(null);
18
+ });
19
+
20
+ afterEach(() => {
21
+ vi.unstubAllEnvs();
22
+ });
23
+
24
+ it("should instantiate successfully when API key is passed to constructor", () => {
25
+ const mindsim = new MindSim("test-api-key-const");
26
+ expect(mindsim).toBeInstanceOf(MindSim);
27
+ expect(mindsim.minds).toBeDefined();
28
+ });
29
+
30
+ it("should instantiate successfully when API key is in config/env (via loadApiKey)", () => {
31
+ vi.mocked(configModule.loadApiKey).mockReturnValue("test-api-key-env");
32
+ const mindsim = new MindSim();
33
+ expect(mindsim).toBeInstanceOf(MindSim);
34
+ });
35
+
36
+ it("should throw error if no API key is provided or found in env", () => {
37
+ expect(() => {
38
+ new MindSim();
39
+ }).toThrow("API Key not found");
40
+ });
41
+ });
@@ -0,0 +1,129 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
5
+ import { getApiBaseUrl, getAuthConfig, loadApiKey, saveApiKey } from "../src/config";
6
+
7
+ // Mock Node built-ins
8
+ vi.mock("node:fs");
9
+ vi.mock("node:os");
10
+
11
+ const mockedFs = fs as Mocked<typeof fs>;
12
+ const mockedOs = os as Mocked<typeof os>;
13
+
14
+ describe("Config Module", () => {
15
+ const MOCK_HOME_DIR = "/mock/home";
16
+ const EXPECTED_CONFIG_DIR = path.join(MOCK_HOME_DIR, ".mindsim");
17
+ const EXPECTED_CONFIG_FILE = path.join(EXPECTED_CONFIG_DIR, "config");
18
+
19
+ beforeEach(() => {
20
+ vi.resetModules();
21
+ mockedOs.homedir.mockReturnValue(MOCK_HOME_DIR);
22
+ });
23
+
24
+ afterEach(() => {
25
+ vi.unstubAllEnvs();
26
+ vi.clearAllMocks();
27
+ });
28
+
29
+ describe("loadApiKey", () => {
30
+ it("should prioritize API key from process.env", () => {
31
+ vi.stubEnv("MINDSIM_API_KEY", "env-key-123");
32
+
33
+ // Even if file exists, env should win
34
+ mockedFs.existsSync.mockReturnValue(true);
35
+ mockedFs.readFileSync.mockReturnValue(JSON.stringify({ apiKey: "file-key-456" }));
36
+
37
+ const key = loadApiKey();
38
+ expect(key).toBe("env-key-123");
39
+ });
40
+
41
+ it("should load from config file if env var is missing", () => {
42
+ vi.stubEnv("MINDSIM_API_KEY", ""); // Ensure env is empty
43
+ mockedFs.existsSync.mockReturnValue(true);
44
+ mockedFs.readFileSync.mockReturnValue(JSON.stringify({ apiKey: "file-key-456" }));
45
+
46
+ const key = loadApiKey();
47
+ expect(key).toBe("file-key-456");
48
+ expect(mockedFs.readFileSync).toHaveBeenCalledWith(EXPECTED_CONFIG_FILE, "utf-8");
49
+ });
50
+
51
+ it("should return null if neither env var nor file exists", () => {
52
+ vi.stubEnv("MINDSIM_API_KEY", "");
53
+ mockedFs.existsSync.mockReturnValue(false);
54
+
55
+ const key = loadApiKey();
56
+ expect(key).toBeNull();
57
+ });
58
+
59
+ it("should return null and log error if file read fails", () => {
60
+ vi.stubEnv("MINDSIM_API_KEY", "");
61
+ mockedFs.existsSync.mockReturnValue(true);
62
+ mockedFs.readFileSync.mockImplementation(() => {
63
+ throw new Error("File permission denied");
64
+ });
65
+
66
+ // Spy on console.error to suppress output during test
67
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
68
+
69
+ const key = loadApiKey();
70
+ expect(key).toBeNull();
71
+ expect(consoleSpy).toHaveBeenCalled();
72
+ });
73
+ });
74
+
75
+ describe("saveApiKey", () => {
76
+ it("should create directory and write config file", () => {
77
+ const NEW_KEY = "new-api-key-789";
78
+ mockedFs.existsSync.mockReturnValue(false); // Dir doesn't exist
79
+
80
+ saveApiKey(NEW_KEY);
81
+
82
+ expect(mockedFs.mkdirSync).toHaveBeenCalledWith(EXPECTED_CONFIG_DIR, { recursive: true });
83
+ expect(mockedFs.writeFileSync).toHaveBeenCalledWith(
84
+ EXPECTED_CONFIG_FILE,
85
+ JSON.stringify({ apiKey: NEW_KEY }, null, 2),
86
+ );
87
+ });
88
+
89
+ it("should write config file without creating dir if dir exists", () => {
90
+ mockedFs.existsSync.mockReturnValue(true); // Dir exists
91
+
92
+ saveApiKey("key");
93
+
94
+ expect(mockedFs.mkdirSync).not.toHaveBeenCalled();
95
+ expect(mockedFs.writeFileSync).toHaveBeenCalled();
96
+ });
97
+ });
98
+
99
+ describe("getAuthConfig", () => {
100
+ it("should return default values when env vars are not set", () => {
101
+ const config = getAuthConfig();
102
+ expect(config.authServerUrl).toBe("https://app.mindsim.com/sdk/authorize");
103
+ expect(config.authRedirectPath).toBe("http://localhost:4242/callback");
104
+ expect(config.listenPort).toBe("4242");
105
+ });
106
+
107
+ it("should return env overrides when set", () => {
108
+ vi.stubEnv("MINDSIM_AUTH_SERVER_URL", "https://dev.mindsim.com/auth");
109
+ vi.stubEnv("MINDSIM_AUTH_REDIRECT_URI", "http://custom-callback");
110
+ vi.stubEnv("MINDSIM_AUTH_PORT", "8080");
111
+
112
+ const config = getAuthConfig();
113
+ expect(config.authServerUrl).toBe("https://dev.mindsim.com/auth");
114
+ expect(config.authRedirectPath).toBe("http://custom-callback");
115
+ expect(config.listenPort).toBe("8080");
116
+ });
117
+ });
118
+
119
+ describe("getApiBaseUrl", () => {
120
+ it("should return default URL", () => {
121
+ expect(getApiBaseUrl()).toBe("https://app.mindsim.com/api/public/v1");
122
+ });
123
+
124
+ it("should return override from environment", () => {
125
+ vi.stubEnv("MIND_SIM_API_BASE_URL", "https://staging.api.mindsim.com");
126
+ expect(getApiBaseUrl()).toBe("https://staging.api.mindsim.com");
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,119 @@
1
+ import axios from "axios";
2
+ import { beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
3
+ import { MindSim } from "../../src/index";
4
+ import type { Mind } from "../../src/types";
5
+
6
+ vi.mock("axios");
7
+ const mockedAxios = axios as Mocked<typeof axios>;
8
+
9
+ describe("MindSim Minds Resource", () => {
10
+ let mindsim: MindSim;
11
+
12
+ const MIND_ID = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d";
13
+ const TAG_ID_1 = "56255954-189f-4a1e-a355-08a1704af943";
14
+
15
+ const mockClient = {
16
+ get: vi.fn(),
17
+ post: vi.fn(),
18
+ put: vi.fn(),
19
+ delete: vi.fn(),
20
+ defaults: { headers: { common: {} } },
21
+ interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() } },
22
+ };
23
+
24
+ beforeEach(() => {
25
+ // Mock axios.create to return our mockClient
26
+ mockedAxios.create.mockReturnValue(mockClient as any);
27
+ mindsim = new MindSim("fake-key");
28
+ });
29
+
30
+ it("create() should transform tag array to comma-separated string", async () => {
31
+ const mockResponse: Mind = {
32
+ id: MIND_ID,
33
+ name: "Bobby",
34
+ scope: "private",
35
+ isSelf: false,
36
+ tags: [],
37
+ };
38
+
39
+ mockClient.post.mockResolvedValue({ data: { mind: mockResponse } });
40
+
41
+ const result = await mindsim.minds.create({
42
+ name: "Bobby",
43
+ tags: ["dev", "ts"],
44
+ });
45
+
46
+ expect(mockClient.post).toHaveBeenCalledWith("/minds", {
47
+ name: "Bobby",
48
+ tags: "dev,ts", // Verify transformation
49
+ });
50
+ expect(result).toEqual(mockResponse);
51
+ expect(result.id).toMatch(
52
+ /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
53
+ );
54
+ });
55
+
56
+ it("update() should PUT with transformed tags", async () => {
57
+ const mockResponse: Mind = {
58
+ id: MIND_ID,
59
+ name: "Bobby Updated",
60
+ scope: "private",
61
+ isSelf: false,
62
+ tags: [],
63
+ };
64
+
65
+ mockClient.put.mockResolvedValue({ data: { mind: mockResponse } });
66
+
67
+ const result = await mindsim.minds.update(MIND_ID, {
68
+ name: "Bobby Updated",
69
+ tags: ["lead", "admin"],
70
+ });
71
+
72
+ expect(mockClient.put).toHaveBeenCalledWith(`/minds/${MIND_ID}`, {
73
+ name: "Bobby Updated",
74
+ tags: "lead,admin", // Verify transformation to string
75
+ });
76
+ expect(result.name).toBe("Bobby Updated");
77
+ });
78
+
79
+ it("delete() should DELETE mind", async () => {
80
+ mockClient.delete.mockResolvedValue({ data: { success: true } });
81
+
82
+ const result = await mindsim.minds.delete(MIND_ID);
83
+
84
+ expect(mockClient.delete).toHaveBeenCalledWith(`/minds/${MIND_ID}`);
85
+ expect(result.success).toBe(true);
86
+ });
87
+
88
+ it("list() should handle tag filtering", async () => {
89
+ const mockMinds = [{ id: MIND_ID, name: "A" }];
90
+ mockClient.get.mockResolvedValue({ data: { minds: mockMinds } });
91
+
92
+ await mindsim.minds.list({ tags: ["hero"] });
93
+
94
+ expect(mockClient.get).toHaveBeenCalledWith("/minds", {
95
+ params: { tagNames: "hero" },
96
+ });
97
+ });
98
+
99
+ it("get() should retrieve a mind by ID", async () => {
100
+ const mockMind = { id: MIND_ID, name: "Sarah" };
101
+ mockClient.get.mockResolvedValue({ data: mockMind });
102
+
103
+ const result = await mindsim.minds.get(MIND_ID);
104
+ expect(mockClient.get).toHaveBeenCalledWith(`/minds/${MIND_ID}`);
105
+ expect(result.id).toBe(MIND_ID);
106
+ });
107
+
108
+ it("setTags() should PUT to correct endpoint", async () => {
109
+ mockClient.put.mockResolvedValue({
110
+ data: { mind: { id: MIND_ID, tags: [{ id: TAG_ID_1, name: "one" }] } },
111
+ });
112
+
113
+ await mindsim.minds.setTags(MIND_ID, { tags: ["one", "two"] });
114
+
115
+ expect(mockClient.put).toHaveBeenCalledWith(`/minds/${MIND_ID}/tags`, {
116
+ tagNames: ["one", "two"],
117
+ });
118
+ });
119
+ });
@@ -0,0 +1,38 @@
1
+ import axios from "axios";
2
+ import { beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
3
+ import { MindSim } from "../../src/index";
4
+
5
+ vi.mock("axios");
6
+ const mockedAxios = axios as Mocked<typeof axios>;
7
+
8
+ describe("MindSim Psychometrics Resource", () => {
9
+ let mindsim: MindSim;
10
+ const SNAPSHOT_ID = "123e4567-e89b-12d3-a456-426614174003";
11
+
12
+ const mockApiClient = {
13
+ get: vi.fn(),
14
+ defaults: { headers: { common: {} } },
15
+ interceptors: { request: { use: vi.fn() }, response: { use: vi.fn() } },
16
+ };
17
+
18
+ beforeEach(() => {
19
+ mockedAxios.create.mockReturnValue(mockApiClient as any);
20
+ mindsim = new MindSim("fake-key");
21
+ });
22
+
23
+ it("get() should fetch psychometrics for a specific snapshot", async () => {
24
+ const mockResponse = {
25
+ personDetails: { name: "John Doe", mindAssesssmentId: SNAPSHOT_ID },
26
+ dimensionAnalysis: { openness: 0.8 },
27
+ };
28
+
29
+ mockApiClient.get.mockResolvedValue({ data: mockResponse });
30
+
31
+ const result = await mindsim.psychometrics.get(SNAPSHOT_ID);
32
+
33
+ // Note: Endpoint is /snapshots/{id}/psychometrics, not /minds/...
34
+ expect(mockApiClient.get).toHaveBeenCalledWith(`/snapshots/${SNAPSHOT_ID}/psychometrics`);
35
+ expect(result.personDetails.name).toBe("John Doe");
36
+ expect(result.dimensionAnalysis?.openness).toBe(0.8);
37
+ });
38
+ });