@objectstack/cli 4.0.1 → 4.0.2

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 (84) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +17 -0
  3. package/dist/commands/auth/login.d.ts +13 -0
  4. package/dist/commands/auth/login.d.ts.map +1 -0
  5. package/dist/commands/auth/login.js +167 -0
  6. package/dist/commands/auth/login.js.map +1 -0
  7. package/dist/commands/auth/logout.d.ts +10 -0
  8. package/dist/commands/auth/logout.d.ts.map +1 -0
  9. package/dist/commands/auth/logout.js +46 -0
  10. package/dist/commands/auth/logout.js.map +1 -0
  11. package/dist/commands/auth/whoami.d.ts +12 -0
  12. package/dist/commands/auth/whoami.d.ts.map +1 -0
  13. package/dist/commands/auth/whoami.js +76 -0
  14. package/dist/commands/auth/whoami.js.map +1 -0
  15. package/dist/commands/data/create.d.ts +17 -0
  16. package/dist/commands/data/create.d.ts.map +1 -0
  17. package/dist/commands/data/create.js +106 -0
  18. package/dist/commands/data/create.js.map +1 -0
  19. package/dist/commands/data/delete.d.ts +16 -0
  20. package/dist/commands/data/delete.d.ts.map +1 -0
  21. package/dist/commands/data/delete.js +78 -0
  22. package/dist/commands/data/delete.js.map +1 -0
  23. package/dist/commands/data/get.d.ts +16 -0
  24. package/dist/commands/data/get.d.ts.map +1 -0
  25. package/dist/commands/data/get.js +79 -0
  26. package/dist/commands/data/get.js.map +1 -0
  27. package/dist/commands/data/query.d.ts +20 -0
  28. package/dist/commands/data/query.d.ts.map +1 -0
  29. package/dist/commands/data/query.js +118 -0
  30. package/dist/commands/data/query.js.map +1 -0
  31. package/dist/commands/data/update.d.ts +18 -0
  32. package/dist/commands/data/update.d.ts.map +1 -0
  33. package/dist/commands/data/update.js +110 -0
  34. package/dist/commands/data/update.js.map +1 -0
  35. package/dist/commands/meta/delete.d.ts +16 -0
  36. package/dist/commands/meta/delete.d.ts.map +1 -0
  37. package/dist/commands/meta/delete.js +73 -0
  38. package/dist/commands/meta/delete.js.map +1 -0
  39. package/dist/commands/meta/get.d.ts +16 -0
  40. package/dist/commands/meta/get.d.ts.map +1 -0
  41. package/dist/commands/meta/get.js +65 -0
  42. package/dist/commands/meta/get.js.map +1 -0
  43. package/dist/commands/meta/list.d.ts +15 -0
  44. package/dist/commands/meta/list.d.ts.map +1 -0
  45. package/dist/commands/meta/list.js +103 -0
  46. package/dist/commands/meta/list.js.map +1 -0
  47. package/dist/commands/meta/register.d.ts +16 -0
  48. package/dist/commands/meta/register.d.ts.map +1 -0
  49. package/dist/commands/meta/register.js +89 -0
  50. package/dist/commands/meta/register.js.map +1 -0
  51. package/dist/commands/serve.d.ts.map +1 -1
  52. package/dist/commands/serve.js +33 -0
  53. package/dist/commands/serve.js.map +1 -1
  54. package/dist/utils/api-client.d.ts +42 -0
  55. package/dist/utils/api-client.d.ts.map +1 -0
  56. package/dist/utils/api-client.js +53 -0
  57. package/dist/utils/api-client.js.map +1 -0
  58. package/dist/utils/auth-config.d.ts +50 -0
  59. package/dist/utils/auth-config.d.ts.map +1 -0
  60. package/dist/utils/auth-config.js +73 -0
  61. package/dist/utils/auth-config.js.map +1 -0
  62. package/dist/utils/output-formatter.d.ts +9 -0
  63. package/dist/utils/output-formatter.d.ts.map +1 -0
  64. package/dist/utils/output-formatter.js +80 -0
  65. package/dist/utils/output-formatter.js.map +1 -0
  66. package/package.json +17 -12
  67. package/src/commands/auth/login.ts +188 -0
  68. package/src/commands/auth/logout.ts +51 -0
  69. package/src/commands/auth/whoami.ts +85 -0
  70. package/src/commands/data/create.ts +110 -0
  71. package/src/commands/data/delete.ts +84 -0
  72. package/src/commands/data/get.ts +84 -0
  73. package/src/commands/data/query.ts +127 -0
  74. package/src/commands/data/update.ts +114 -0
  75. package/src/commands/meta/delete.ts +79 -0
  76. package/src/commands/meta/get.ts +73 -0
  77. package/src/commands/meta/list.ts +105 -0
  78. package/src/commands/meta/register.ts +97 -0
  79. package/src/commands/serve.ts +38 -0
  80. package/src/utils/api-client.ts +88 -0
  81. package/src/utils/auth-config.ts +107 -0
  82. package/src/utils/output-formatter.ts +91 -0
  83. package/test/remote-api-commands.test.ts +188 -0
  84. package/test/remote-api-utils.test.ts +196 -0
@@ -0,0 +1,196 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { createApiClient, requireAuth } from '../src/utils/api-client';
3
+ import { readAuthConfig, writeAuthConfig, deleteAuthConfig, getCredentialsPath } from '../src/utils/auth-config';
4
+ import { formatOutput } from '../src/utils/output-formatter';
5
+ import * as fs from 'node:fs/promises';
6
+
7
+ // Mock fs module
8
+ vi.mock('node:fs/promises');
9
+
10
+ describe('API Client Utilities', () => {
11
+ describe('createApiClient', () => {
12
+ it('should use provided URL and token', async () => {
13
+ const { client, token } = await createApiClient({
14
+ url: 'https://test.example.com',
15
+ token: 'test-token',
16
+ });
17
+
18
+ expect(client).toBeDefined();
19
+ expect((client as any).baseUrl).toBe('https://test.example.com');
20
+ expect(token).toBe('test-token');
21
+ });
22
+
23
+ it('should default to localhost when no URL provided', async () => {
24
+ const { client } = await createApiClient({});
25
+
26
+ expect(client).toBeDefined();
27
+ expect((client as any).baseUrl).toBe('http://localhost:3000');
28
+ });
29
+
30
+ it('should use environment variables if no options provided', async () => {
31
+ const originalUrl = process.env.OBJECTSTACK_URL;
32
+ const originalToken = process.env.OBJECTSTACK_TOKEN;
33
+
34
+ process.env.OBJECTSTACK_URL = 'https://env.example.com';
35
+ process.env.OBJECTSTACK_TOKEN = 'env-token';
36
+
37
+ const { client, token } = await createApiClient({});
38
+
39
+ expect((client as any).baseUrl).toBe('https://env.example.com');
40
+ expect(token).toBe('env-token');
41
+
42
+ // Restore
43
+ process.env.OBJECTSTACK_URL = originalUrl;
44
+ process.env.OBJECTSTACK_TOKEN = originalToken;
45
+ });
46
+ });
47
+
48
+ describe('requireAuth', () => {
49
+ it('should not throw when token is provided', () => {
50
+ expect(() => requireAuth('valid-token')).not.toThrow();
51
+ });
52
+
53
+ it('should throw when token is missing', () => {
54
+ expect(() => requireAuth(undefined)).toThrow(/Authentication required/);
55
+ });
56
+
57
+ it('should throw when token is empty string', () => {
58
+ expect(() => requireAuth('')).toThrow(/Authentication required/);
59
+ });
60
+ });
61
+ });
62
+
63
+ describe('Auth Config Utilities', () => {
64
+ beforeEach(() => {
65
+ vi.clearAllMocks();
66
+ });
67
+
68
+ describe('getCredentialsPath', () => {
69
+ it('should return path to credentials file', () => {
70
+ const path = getCredentialsPath();
71
+ expect(path).toContain('.objectstack');
72
+ expect(path).toContain('credentials.json');
73
+ });
74
+ });
75
+
76
+ describe('writeAuthConfig', () => {
77
+ it('should write credentials to file with correct permissions', async () => {
78
+ const mockMkdir = vi.mocked(fs.mkdir);
79
+ const mockWriteFile = vi.mocked(fs.writeFile);
80
+
81
+ const config = {
82
+ url: 'https://test.example.com',
83
+ token: 'test-token',
84
+ email: 'user@example.com',
85
+ createdAt: '2024-01-01T00:00:00.000Z',
86
+ };
87
+
88
+ await writeAuthConfig(config);
89
+
90
+ expect(mockMkdir).toHaveBeenCalledWith(
91
+ expect.stringContaining('.objectstack'),
92
+ { recursive: true }
93
+ );
94
+
95
+ expect(mockWriteFile).toHaveBeenCalledWith(
96
+ expect.stringContaining('credentials.json'),
97
+ expect.stringContaining('test-token'),
98
+ { mode: 0o600 }
99
+ );
100
+ });
101
+ });
102
+
103
+ describe('readAuthConfig', () => {
104
+ it('should read and parse credentials file', async () => {
105
+ const mockConfig = {
106
+ url: 'https://test.example.com',
107
+ token: 'test-token',
108
+ email: 'user@example.com',
109
+ createdAt: '2024-01-01T00:00:00.000Z',
110
+ };
111
+
112
+ const mockReadFile = vi.mocked(fs.readFile);
113
+ mockReadFile.mockResolvedValue(JSON.stringify(mockConfig));
114
+
115
+ const config = await readAuthConfig();
116
+
117
+ expect(config).toEqual(mockConfig);
118
+ });
119
+
120
+ it('should throw helpful error when file does not exist', async () => {
121
+ const mockReadFile = vi.mocked(fs.readFile);
122
+ mockReadFile.mockRejectedValue({ code: 'ENOENT' });
123
+
124
+ await expect(readAuthConfig()).rejects.toThrow(/No stored credentials/);
125
+ });
126
+ });
127
+
128
+ describe('deleteAuthConfig', () => {
129
+ it('should delete credentials file', async () => {
130
+ const mockUnlink = vi.mocked(fs.unlink);
131
+ mockUnlink.mockResolvedValue(undefined as any);
132
+
133
+ await deleteAuthConfig();
134
+
135
+ expect(mockUnlink).toHaveBeenCalled();
136
+ expect(mockUnlink).toHaveBeenCalledWith(
137
+ expect.stringContaining('credentials.json')
138
+ );
139
+ });
140
+
141
+ it('should not throw if file does not exist', async () => {
142
+ const mockUnlink = vi.mocked(fs.unlink);
143
+ mockUnlink.mockRejectedValue({ code: 'ENOENT' } as any);
144
+
145
+ await expect(deleteAuthConfig()).resolves.not.toThrow();
146
+ expect(mockUnlink).toHaveBeenCalled();
147
+ });
148
+ });
149
+ });
150
+
151
+ describe('Output Formatter Utilities', () => {
152
+ beforeEach(() => {
153
+ // Spy on console.log
154
+ vi.spyOn(console, 'log').mockImplementation(() => {});
155
+ });
156
+
157
+ it('should format JSON output', () => {
158
+ const data = { name: 'test', value: 123 };
159
+ formatOutput(data, 'json');
160
+
161
+ expect(console.log).toHaveBeenCalledWith(
162
+ expect.stringContaining('"name": "test"')
163
+ );
164
+ });
165
+
166
+ it('should format YAML output', () => {
167
+ const data = { name: 'test', value: 123 };
168
+ formatOutput(data, 'yaml');
169
+
170
+ expect(console.log).toHaveBeenCalled();
171
+ });
172
+
173
+ it('should format table output for arrays', () => {
174
+ const data = [
175
+ { name: 'item1', value: 1 },
176
+ { name: 'item2', value: 2 },
177
+ ];
178
+ formatOutput(data, 'table');
179
+
180
+ expect(console.log).toHaveBeenCalled();
181
+ });
182
+
183
+ it('should format table output for single object', () => {
184
+ const data = { name: 'test', value: 123 };
185
+ formatOutput(data, 'table');
186
+
187
+ expect(console.log).toHaveBeenCalled();
188
+ });
189
+
190
+ it('should handle empty arrays', () => {
191
+ const data: any[] = [];
192
+ formatOutput(data, 'table');
193
+
194
+ expect(console.log).toHaveBeenCalled();
195
+ });
196
+ });