@robosystems/client 0.2.48 → 0.3.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 (190) hide show
  1. package/extensions/InvestorClient.d.ts +91 -0
  2. package/extensions/InvestorClient.js +223 -0
  3. package/extensions/InvestorClient.ts +501 -0
  4. package/extensions/LedgerClient.d.ts +213 -165
  5. package/extensions/LedgerClient.js +315 -393
  6. package/extensions/LedgerClient.ts +887 -564
  7. package/extensions/ReportClient.d.ts +77 -115
  8. package/extensions/ReportClient.js +124 -211
  9. package/extensions/ReportClient.ts +298 -358
  10. package/extensions/config.js +1 -1
  11. package/extensions/config.ts +1 -1
  12. package/extensions/graphql/client.d.ts +46 -0
  13. package/extensions/graphql/client.js +75 -0
  14. package/extensions/graphql/client.ts +82 -0
  15. package/extensions/graphql/generated/graphql.d.ts +1786 -0
  16. package/extensions/graphql/generated/graphql.js +2671 -0
  17. package/extensions/graphql/generated/graphql.ts +4491 -0
  18. package/extensions/graphql/queries/investor/holdings.d.ts +8 -0
  19. package/extensions/graphql/queries/investor/holdings.js +36 -0
  20. package/extensions/graphql/queries/investor/holdings.ts +34 -0
  21. package/extensions/graphql/queries/investor/portfolio.d.ts +4 -0
  22. package/extensions/graphql/queries/investor/portfolio.js +21 -0
  23. package/extensions/graphql/queries/investor/portfolio.ts +19 -0
  24. package/extensions/graphql/queries/investor/portfolios.d.ts +4 -0
  25. package/extensions/graphql/queries/investor/portfolios.js +29 -0
  26. package/extensions/graphql/queries/investor/portfolios.ts +27 -0
  27. package/extensions/graphql/queries/investor/position.d.ts +4 -0
  28. package/extensions/graphql/queries/investor/position.js +33 -0
  29. package/extensions/graphql/queries/investor/position.ts +31 -0
  30. package/extensions/graphql/queries/investor/positions.d.ts +8 -0
  31. package/extensions/graphql/queries/investor/positions.js +57 -0
  32. package/extensions/graphql/queries/investor/positions.ts +55 -0
  33. package/extensions/graphql/queries/investor/securities.d.ts +8 -0
  34. package/extensions/graphql/queries/investor/securities.js +50 -0
  35. package/extensions/graphql/queries/investor/securities.ts +48 -0
  36. package/extensions/graphql/queries/investor/security.d.ts +4 -0
  37. package/extensions/graphql/queries/investor/security.js +26 -0
  38. package/extensions/graphql/queries/investor/security.ts +24 -0
  39. package/extensions/graphql/queries/ledger/accountRollups.d.ts +7 -0
  40. package/extensions/graphql/queries/ledger/accountRollups.js +36 -0
  41. package/extensions/graphql/queries/ledger/accountRollups.ts +34 -0
  42. package/extensions/graphql/queries/ledger/accountTree.d.ts +11 -0
  43. package/extensions/graphql/queries/ledger/accountTree.js +61 -0
  44. package/extensions/graphql/queries/ledger/accountTree.ts +59 -0
  45. package/extensions/graphql/queries/ledger/accounts.d.ts +8 -0
  46. package/extensions/graphql/queries/ledger/accounts.js +45 -0
  47. package/extensions/graphql/queries/ledger/accounts.ts +43 -0
  48. package/extensions/graphql/queries/ledger/closingBookStructures.d.ts +6 -0
  49. package/extensions/graphql/queries/ledger/closingBookStructures.js +27 -0
  50. package/extensions/graphql/queries/ledger/closingBookStructures.ts +25 -0
  51. package/extensions/graphql/queries/ledger/elements.d.ts +6 -0
  52. package/extensions/graphql/queries/ledger/elements.js +56 -0
  53. package/extensions/graphql/queries/ledger/elements.ts +54 -0
  54. package/extensions/graphql/queries/ledger/entities.d.ts +7 -0
  55. package/extensions/graphql/queries/ledger/entities.js +31 -0
  56. package/extensions/graphql/queries/ledger/entities.ts +29 -0
  57. package/extensions/graphql/queries/ledger/entity.d.ts +14 -0
  58. package/extensions/graphql/queries/ledger/entity.js +55 -0
  59. package/extensions/graphql/queries/ledger/entity.ts +54 -0
  60. package/extensions/graphql/queries/ledger/fiscalCalendar.d.ts +8 -0
  61. package/extensions/graphql/queries/ledger/fiscalCalendar.js +35 -0
  62. package/extensions/graphql/queries/ledger/fiscalCalendar.ts +33 -0
  63. package/extensions/graphql/queries/ledger/mappedTrialBalance.d.ts +9 -0
  64. package/extensions/graphql/queries/ledger/mappedTrialBalance.js +29 -0
  65. package/extensions/graphql/queries/ledger/mappedTrialBalance.ts +27 -0
  66. package/extensions/graphql/queries/ledger/mapping.d.ts +5 -0
  67. package/extensions/graphql/queries/ledger/mapping.js +35 -0
  68. package/extensions/graphql/queries/ledger/mapping.ts +33 -0
  69. package/extensions/graphql/queries/ledger/mappingCoverage.d.ts +5 -0
  70. package/extensions/graphql/queries/ledger/mappingCoverage.js +22 -0
  71. package/extensions/graphql/queries/ledger/mappingCoverage.ts +20 -0
  72. package/extensions/graphql/queries/ledger/mappings.d.ts +6 -0
  73. package/extensions/graphql/queries/ledger/mappings.js +23 -0
  74. package/extensions/graphql/queries/ledger/mappings.ts +21 -0
  75. package/extensions/graphql/queries/ledger/periodCloseStatus.d.ts +6 -0
  76. package/extensions/graphql/queries/ledger/periodCloseStatus.js +29 -0
  77. package/extensions/graphql/queries/ledger/periodCloseStatus.ts +27 -0
  78. package/extensions/graphql/queries/ledger/periodDrafts.d.ts +8 -0
  79. package/extensions/graphql/queries/ledger/periodDrafts.js +45 -0
  80. package/extensions/graphql/queries/ledger/periodDrafts.ts +43 -0
  81. package/extensions/graphql/queries/ledger/publishList.d.ts +5 -0
  82. package/extensions/graphql/queries/ledger/publishList.js +29 -0
  83. package/extensions/graphql/queries/ledger/publishList.ts +27 -0
  84. package/extensions/graphql/queries/ledger/publishLists.d.ts +5 -0
  85. package/extensions/graphql/queries/ledger/publishLists.js +29 -0
  86. package/extensions/graphql/queries/ledger/publishLists.ts +27 -0
  87. package/extensions/graphql/queries/ledger/report.d.ts +5 -0
  88. package/extensions/graphql/queries/ledger/report.js +40 -0
  89. package/extensions/graphql/queries/ledger/report.ts +38 -0
  90. package/extensions/graphql/queries/ledger/reportingTaxonomy.d.ts +6 -0
  91. package/extensions/graphql/queries/ledger/reportingTaxonomy.js +27 -0
  92. package/extensions/graphql/queries/ledger/reportingTaxonomy.ts +25 -0
  93. package/extensions/graphql/queries/ledger/reports.d.ts +8 -0
  94. package/extensions/graphql/queries/ledger/reports.js +45 -0
  95. package/extensions/graphql/queries/ledger/reports.ts +43 -0
  96. package/extensions/graphql/queries/ledger/scheduleFacts.d.ts +7 -0
  97. package/extensions/graphql/queries/ledger/scheduleFacts.js +24 -0
  98. package/extensions/graphql/queries/ledger/scheduleFacts.ts +22 -0
  99. package/extensions/graphql/queries/ledger/schedules.d.ts +6 -0
  100. package/extensions/graphql/queries/ledger/schedules.js +24 -0
  101. package/extensions/graphql/queries/ledger/schedules.ts +22 -0
  102. package/extensions/graphql/queries/ledger/statement.d.ts +9 -0
  103. package/extensions/graphql/queries/ledger/statement.js +43 -0
  104. package/extensions/graphql/queries/ledger/statement.ts +41 -0
  105. package/extensions/graphql/queries/ledger/structures.d.ts +5 -0
  106. package/extensions/graphql/queries/ledger/structures.js +22 -0
  107. package/extensions/graphql/queries/ledger/structures.ts +20 -0
  108. package/extensions/graphql/queries/ledger/summary.d.ts +7 -0
  109. package/extensions/graphql/queries/ledger/summary.js +25 -0
  110. package/extensions/graphql/queries/ledger/summary.ts +23 -0
  111. package/extensions/graphql/queries/ledger/taxonomies.d.ts +5 -0
  112. package/extensions/graphql/queries/ledger/taxonomies.js +28 -0
  113. package/extensions/graphql/queries/ledger/taxonomies.ts +26 -0
  114. package/extensions/graphql/queries/ledger/transaction.d.ts +6 -0
  115. package/extensions/graphql/queries/ledger/transaction.js +49 -0
  116. package/extensions/graphql/queries/ledger/transaction.ts +47 -0
  117. package/extensions/graphql/queries/ledger/transactions.d.ts +7 -0
  118. package/extensions/graphql/queries/ledger/transactions.js +49 -0
  119. package/extensions/graphql/queries/ledger/transactions.ts +47 -0
  120. package/extensions/graphql/queries/ledger/trialBalance.d.ts +7 -0
  121. package/extensions/graphql/queries/ledger/trialBalance.js +28 -0
  122. package/extensions/graphql/queries/ledger/trialBalance.ts +26 -0
  123. package/extensions/graphql/queries/ledger/unmappedElements.d.ts +8 -0
  124. package/extensions/graphql/queries/ledger/unmappedElements.js +29 -0
  125. package/extensions/graphql/queries/ledger/unmappedElements.ts +27 -0
  126. package/extensions/hooks.d.ts +1 -1
  127. package/extensions/index.d.ts +5 -1
  128. package/extensions/index.js +13 -1
  129. package/extensions/index.ts +14 -0
  130. package/index.ts +2 -2
  131. package/package.json +14 -5
  132. package/sdk/index.d.ts +2 -2
  133. package/sdk/index.js +38 -65
  134. package/sdk/index.ts +2 -2
  135. package/sdk/sdk.gen.d.ts +101 -220
  136. package/sdk/sdk.gen.js +251 -445
  137. package/sdk/sdk.gen.ts +216 -409
  138. package/sdk/types.gen.d.ts +4586 -7045
  139. package/sdk/types.gen.ts +4702 -7334
  140. package/sdk.gen.d.ts +101 -220
  141. package/sdk.gen.js +251 -445
  142. package/sdk.gen.ts +216 -409
  143. package/types.gen.d.ts +4586 -7045
  144. package/types.gen.ts +4702 -7334
  145. package/extensions/AgentClient.test.ts +0 -403
  146. package/extensions/LedgerClient.test.ts +0 -1138
  147. package/extensions/OperationClient.test.ts +0 -365
  148. package/extensions/QueryClient.test.ts +0 -432
  149. package/extensions/ReportClient.test.ts +0 -828
  150. package/extensions/SSEClient.test.ts +0 -375
  151. package/extensions/config.test.ts +0 -199
  152. package/extensions/hooks.test.ts +0 -373
  153. package/extensions/index.test.ts +0 -420
  154. package/sdk-extensions/AgentClient.d.ts +0 -82
  155. package/sdk-extensions/AgentClient.js +0 -218
  156. package/sdk-extensions/AgentClient.test.ts +0 -403
  157. package/sdk-extensions/AgentClient.ts +0 -321
  158. package/sdk-extensions/LedgerClient.d.ts +0 -242
  159. package/sdk-extensions/LedgerClient.js +0 -496
  160. package/sdk-extensions/LedgerClient.test.ts +0 -1138
  161. package/sdk-extensions/LedgerClient.ts +0 -800
  162. package/sdk-extensions/OperationClient.d.ts +0 -65
  163. package/sdk-extensions/OperationClient.js +0 -251
  164. package/sdk-extensions/OperationClient.test.ts +0 -365
  165. package/sdk-extensions/OperationClient.ts +0 -324
  166. package/sdk-extensions/QueryClient.d.ts +0 -52
  167. package/sdk-extensions/QueryClient.js +0 -313
  168. package/sdk-extensions/QueryClient.test.ts +0 -432
  169. package/sdk-extensions/QueryClient.ts +0 -420
  170. package/sdk-extensions/README.md +0 -901
  171. package/sdk-extensions/ReportClient.d.ts +0 -151
  172. package/sdk-extensions/ReportClient.js +0 -278
  173. package/sdk-extensions/ReportClient.test.ts +0 -828
  174. package/sdk-extensions/ReportClient.ts +0 -486
  175. package/sdk-extensions/SSEClient.d.ts +0 -62
  176. package/sdk-extensions/SSEClient.js +0 -154
  177. package/sdk-extensions/SSEClient.test.ts +0 -375
  178. package/sdk-extensions/SSEClient.ts +0 -210
  179. package/sdk-extensions/config.d.ts +0 -57
  180. package/sdk-extensions/config.js +0 -152
  181. package/sdk-extensions/config.test.ts +0 -199
  182. package/sdk-extensions/config.ts +0 -175
  183. package/sdk-extensions/hooks.d.ts +0 -110
  184. package/sdk-extensions/hooks.js +0 -384
  185. package/sdk-extensions/hooks.test.ts +0 -373
  186. package/sdk-extensions/hooks.ts +0 -459
  187. package/sdk-extensions/index.d.ts +0 -63
  188. package/sdk-extensions/index.js +0 -164
  189. package/sdk-extensions/index.test.ts +0 -420
  190. package/sdk-extensions/index.ts +0 -203
@@ -1,828 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
2
- import type { Report } from './ReportClient'
3
- import { ReportClient } from './ReportClient'
4
-
5
- // Helper to create proper mock Response objects
6
- function createMockResponse(data: any, options: { ok?: boolean; status?: number } = {}) {
7
- return {
8
- ok: options.ok ?? true,
9
- status: options.status ?? 200,
10
- statusText: options.status === 200 ? 'OK' : 'Error',
11
- headers: new Headers({ 'content-type': 'application/json' }),
12
- json: async () => data,
13
- text: async () => JSON.stringify(data),
14
- blob: async () => new Blob([JSON.stringify(data)]),
15
- arrayBuffer: async () => new TextEncoder().encode(JSON.stringify(data)).buffer,
16
- }
17
- }
18
-
19
- // Reusable mock report response (snake_case API format)
20
- const mockReportResponse = {
21
- id: 'rpt_1',
22
- name: 'Q1 2025 Report',
23
- taxonomy_id: 'tax_usgaap_reporting',
24
- generation_status: 'completed',
25
- period_type: 'quarterly',
26
- period_start: '2025-01-01',
27
- period_end: '2025-03-31',
28
- comparative: true,
29
- mapping_id: 'map_1',
30
- ai_generated: false,
31
- created_at: '2025-04-01T00:00:00Z',
32
- last_generated: '2025-04-01T01:00:00Z',
33
- structures: [
34
- { id: 'str_1', name: 'Income Statement', structure_type: 'income_statement' },
35
- { id: 'str_2', name: 'Balance Sheet', structure_type: 'balance_sheet' },
36
- ],
37
- entity_name: 'ACME Corp',
38
- source_graph_id: null,
39
- source_report_id: null,
40
- shared_at: null,
41
- }
42
-
43
- describe('ReportClient', () => {
44
- let client: ReportClient
45
- let mockFetch: any
46
-
47
- beforeEach(() => {
48
- client = new ReportClient({
49
- baseUrl: 'http://localhost:8000',
50
- token: 'test-token',
51
- headers: { 'X-API-Key': 'test-key' },
52
- })
53
-
54
- mockFetch = vi.fn()
55
- global.fetch = mockFetch
56
- globalThis.fetch = mockFetch
57
- vi.clearAllMocks()
58
- })
59
-
60
- afterEach(() => {
61
- vi.restoreAllMocks()
62
- })
63
-
64
- // ── create ────────────────────────────────────────────────────────────
65
-
66
- describe('create', () => {
67
- it('should create report and return transformed result', async () => {
68
- mockFetch.mockResolvedValueOnce(createMockResponse(mockReportResponse))
69
-
70
- const result = await client.create('graph_1', {
71
- name: 'Q1 2025 Report',
72
- mappingId: 'map_1',
73
- periodStart: '2025-01-01',
74
- periodEnd: '2025-03-31',
75
- })
76
-
77
- expect(result.id).toBe('rpt_1')
78
- expect(result.name).toBe('Q1 2025 Report')
79
- expect(result.taxonomyId).toBe('tax_usgaap_reporting')
80
- expect(result.generationStatus).toBe('completed')
81
- expect(result.periodType).toBe('quarterly')
82
- expect(result.periodStart).toBe('2025-01-01')
83
- expect(result.periodEnd).toBe('2025-03-31')
84
- expect(result.comparative).toBe(true)
85
- expect(result.mappingId).toBe('map_1')
86
- expect(result.aiGenerated).toBe(false)
87
- expect(result.structures).toHaveLength(2)
88
- expect(result.structures[0]).toEqual({
89
- id: 'str_1',
90
- name: 'Income Statement',
91
- structureType: 'income_statement',
92
- })
93
- expect(result.entityName).toBe('ACME Corp')
94
- expect(result.sourceGraphId).toBeNull()
95
- })
96
-
97
- it('should create with custom options', async () => {
98
- mockFetch.mockResolvedValueOnce(createMockResponse(mockReportResponse))
99
-
100
- const result = await client.create('graph_1', {
101
- name: 'Annual',
102
- mappingId: 'map_1',
103
- periodStart: '2025-01-01',
104
- periodEnd: '2025-12-31',
105
- taxonomyId: 'custom_tax',
106
- periodType: 'annual',
107
- comparative: false,
108
- })
109
-
110
- expect(result.id).toBe('rpt_1')
111
- expect(mockFetch).toHaveBeenCalledTimes(1)
112
- })
113
-
114
- it('should create with explicit periods', async () => {
115
- mockFetch.mockResolvedValueOnce(createMockResponse(mockReportResponse))
116
-
117
- const result = await client.create('graph_1', {
118
- name: 'Multi-period',
119
- mappingId: 'map_1',
120
- periodStart: '2024-01-01',
121
- periodEnd: '2025-03-31',
122
- periods: [
123
- { start: '2024-01-01', end: '2024-12-31', label: 'FY2024' },
124
- { start: '2025-01-01', end: '2025-03-31', label: 'Q1 2025' },
125
- ],
126
- })
127
-
128
- expect(result.id).toBe('rpt_1')
129
- expect(mockFetch).toHaveBeenCalledTimes(1)
130
- })
131
-
132
- it('should throw on error', async () => {
133
- mockFetch.mockResolvedValueOnce(
134
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
135
- )
136
-
137
- await expect(
138
- client.create('graph_1', {
139
- name: 'Test',
140
- mappingId: 'map_1',
141
- periodStart: '2025-01-01',
142
- periodEnd: '2025-03-31',
143
- })
144
- ).rejects.toThrow('Create report failed')
145
- })
146
- })
147
-
148
- // ── list ──────────────────────────────────────────────────────────────
149
-
150
- describe('list', () => {
151
- it('should return transformed report list', async () => {
152
- mockFetch.mockResolvedValueOnce(
153
- createMockResponse({
154
- reports: [mockReportResponse],
155
- })
156
- )
157
-
158
- const result = await client.list('graph_1')
159
-
160
- expect(result).toHaveLength(1)
161
- expect(result[0].id).toBe('rpt_1')
162
- expect(result[0].structures).toHaveLength(2)
163
- })
164
-
165
- it('should return empty array when no reports', async () => {
166
- mockFetch.mockResolvedValueOnce(createMockResponse({ reports: [] }))
167
-
168
- const result = await client.list('graph_1')
169
-
170
- expect(result).toEqual([])
171
- })
172
-
173
- it('should handle shared reports in list', async () => {
174
- mockFetch.mockResolvedValueOnce(
175
- createMockResponse({
176
- reports: [
177
- {
178
- ...mockReportResponse,
179
- id: 'rpt_shared',
180
- source_graph_id: 'other_graph',
181
- source_report_id: 'rpt_orig',
182
- shared_at: '2025-04-05T00:00:00Z',
183
- entity_name: 'Other Corp',
184
- },
185
- ],
186
- })
187
- )
188
-
189
- const result = await client.list('graph_1')
190
-
191
- expect(result[0].sourceGraphId).toBe('other_graph')
192
- expect(result[0].sourceReportId).toBe('rpt_orig')
193
- expect(result[0].sharedAt).toBe('2025-04-05T00:00:00Z')
194
- expect(result[0].entityName).toBe('Other Corp')
195
- })
196
-
197
- it('should throw on error', async () => {
198
- mockFetch.mockResolvedValueOnce(
199
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
200
- )
201
-
202
- await expect(client.list('graph_1')).rejects.toThrow('List reports failed')
203
- })
204
- })
205
-
206
- // ── get ───────────────────────────────────────────────────────────────
207
-
208
- describe('get', () => {
209
- it('should return single transformed report', async () => {
210
- mockFetch.mockResolvedValueOnce(createMockResponse(mockReportResponse))
211
-
212
- const result = await client.get('graph_1', 'rpt_1')
213
-
214
- expect(result.id).toBe('rpt_1')
215
- expect(result.name).toBe('Q1 2025 Report')
216
- })
217
-
218
- it('should handle missing optional fields', async () => {
219
- mockFetch.mockResolvedValueOnce(
220
- createMockResponse({
221
- id: 'rpt_2',
222
- name: 'Minimal Report',
223
- taxonomy_id: 'tax_usgaap',
224
- generation_status: 'pending',
225
- period_type: 'quarterly',
226
- comparative: false,
227
- created_at: '2025-04-01T00:00:00Z',
228
- })
229
- )
230
-
231
- const result = await client.get('graph_1', 'rpt_2')
232
-
233
- expect(result.periodStart).toBeNull()
234
- expect(result.periodEnd).toBeNull()
235
- expect(result.mappingId).toBeNull()
236
- expect(result.aiGenerated).toBe(false)
237
- expect(result.lastGenerated).toBeNull()
238
- expect(result.structures).toEqual([])
239
- expect(result.entityName).toBeNull()
240
- expect(result.sourceGraphId).toBeNull()
241
- })
242
-
243
- it('should throw on error', async () => {
244
- mockFetch.mockResolvedValueOnce(
245
- createMockResponse({ detail: 'Not found' }, { ok: false, status: 404 })
246
- )
247
-
248
- await expect(client.get('graph_1', 'rpt_bad')).rejects.toThrow('Get report failed')
249
- })
250
- })
251
-
252
- // ── statement ─────────────────────────────────────────────────────────
253
-
254
- describe('statement', () => {
255
- it('should return transformed statement data', async () => {
256
- mockFetch.mockResolvedValueOnce(
257
- createMockResponse({
258
- report_id: 'rpt_1',
259
- structure_id: 'str_1',
260
- structure_name: 'Income Statement',
261
- structure_type: 'income_statement',
262
- periods: [
263
- { start: '2025-01-01', end: '2025-03-31', label: 'Q1 2025' },
264
- { start: '2024-01-01', end: '2024-03-31', label: 'Q1 2024' },
265
- ],
266
- rows: [
267
- {
268
- element_id: 'elem_rev',
269
- element_qname: 'us-gaap:Revenue',
270
- element_name: 'Revenue',
271
- classification: 'revenue',
272
- values: [500000, 420000],
273
- is_subtotal: false,
274
- depth: 0,
275
- },
276
- {
277
- element_id: 'elem_total',
278
- element_qname: 'us-gaap:NetIncome',
279
- element_name: 'Net Income',
280
- classification: 'income',
281
- values: [75000, 60000],
282
- is_subtotal: true,
283
- depth: 0,
284
- },
285
- ],
286
- validation: {
287
- passed: true,
288
- checks: ['Assets = Liabilities + Equity'],
289
- failures: [],
290
- warnings: ['Some items unmapped'],
291
- },
292
- unmapped_count: 3,
293
- })
294
- )
295
-
296
- const result = await client.statement('graph_1', 'rpt_1', 'income_statement')
297
-
298
- expect(result.reportId).toBe('rpt_1')
299
- expect(result.structureId).toBe('str_1')
300
- expect(result.structureName).toBe('Income Statement')
301
- expect(result.structureType).toBe('income_statement')
302
-
303
- expect(result.periods).toHaveLength(2)
304
- expect(result.periods[0]).toEqual({
305
- start: '2025-01-01',
306
- end: '2025-03-31',
307
- label: 'Q1 2025',
308
- })
309
-
310
- expect(result.rows).toHaveLength(2)
311
- expect(result.rows[0]).toEqual({
312
- elementId: 'elem_rev',
313
- elementQname: 'us-gaap:Revenue',
314
- elementName: 'Revenue',
315
- classification: 'revenue',
316
- values: [500000, 420000],
317
- isSubtotal: false,
318
- depth: 0,
319
- })
320
- expect(result.rows[1].isSubtotal).toBe(true)
321
-
322
- expect(result.validation).not.toBeNull()
323
- expect(result.validation!.passed).toBe(true)
324
- expect(result.validation!.checks).toEqual(['Assets = Liabilities + Equity'])
325
- expect(result.validation!.warnings).toEqual(['Some items unmapped'])
326
-
327
- expect(result.unmappedCount).toBe(3)
328
- })
329
-
330
- it('should handle null validation', async () => {
331
- mockFetch.mockResolvedValueOnce(
332
- createMockResponse({
333
- report_id: 'rpt_1',
334
- structure_id: 'str_1',
335
- structure_name: 'Balance Sheet',
336
- structure_type: 'balance_sheet',
337
- periods: [],
338
- rows: [],
339
- validation: null,
340
- unmapped_count: 0,
341
- })
342
- )
343
-
344
- const result = await client.statement('graph_1', 'rpt_1', 'balance_sheet')
345
-
346
- expect(result.validation).toBeNull()
347
- })
348
-
349
- it('should default missing row fields', async () => {
350
- mockFetch.mockResolvedValueOnce(
351
- createMockResponse({
352
- report_id: 'rpt_1',
353
- structure_id: 'str_1',
354
- structure_name: 'IS',
355
- structure_type: 'income_statement',
356
- periods: [],
357
- rows: [
358
- {
359
- element_id: 'elem_1',
360
- element_qname: 'gaap:X',
361
- element_name: 'X',
362
- classification: 'revenue',
363
- },
364
- ],
365
- })
366
- )
367
-
368
- const result = await client.statement('graph_1', 'rpt_1', 'income_statement')
369
-
370
- expect(result.rows[0].values).toEqual([])
371
- expect(result.rows[0].isSubtotal).toBe(false)
372
- expect(result.rows[0].depth).toBe(0)
373
- })
374
-
375
- it('should throw on error', async () => {
376
- mockFetch.mockResolvedValueOnce(
377
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
378
- )
379
-
380
- await expect(client.statement('graph_1', 'rpt_1', 'income_statement')).rejects.toThrow(
381
- 'Get statement failed'
382
- )
383
- })
384
- })
385
-
386
- // ── regenerate ────────────────────────────────────────────────────────
387
-
388
- describe('regenerate', () => {
389
- it('should regenerate report with new dates', async () => {
390
- mockFetch.mockResolvedValueOnce(
391
- createMockResponse({
392
- ...mockReportResponse,
393
- period_start: '2025-04-01',
394
- period_end: '2025-06-30',
395
- last_generated: '2025-07-01T00:00:00Z',
396
- })
397
- )
398
-
399
- const result = await client.regenerate('graph_1', 'rpt_1', '2025-04-01', '2025-06-30')
400
-
401
- expect(result.periodStart).toBe('2025-04-01')
402
- expect(result.periodEnd).toBe('2025-06-30')
403
- expect(mockFetch).toHaveBeenCalledTimes(1)
404
- })
405
-
406
- it('should throw on error', async () => {
407
- mockFetch.mockResolvedValueOnce(
408
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
409
- )
410
-
411
- await expect(
412
- client.regenerate('graph_1', 'rpt_1', '2025-04-01', '2025-06-30')
413
- ).rejects.toThrow('Regenerate report failed')
414
- })
415
- })
416
-
417
- // ── delete ────────────────────────────────────────────────────────────
418
-
419
- describe('delete', () => {
420
- it('should delete report', async () => {
421
- mockFetch.mockResolvedValueOnce(createMockResponse({ success: true }))
422
-
423
- await expect(client.delete('graph_1', 'rpt_1')).resolves.toBeUndefined()
424
- })
425
-
426
- it('should throw on error', async () => {
427
- mockFetch.mockResolvedValueOnce(
428
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
429
- )
430
-
431
- await expect(client.delete('graph_1', 'rpt_1')).rejects.toThrow('Delete report failed')
432
- })
433
- })
434
-
435
- // ── share ─────────────────────────────────────────────────────────────
436
-
437
- describe('share', () => {
438
- it('should share report and return results', async () => {
439
- mockFetch.mockResolvedValueOnce(
440
- createMockResponse({
441
- report_id: 'rpt_1',
442
- results: [
443
- {
444
- target_graph_id: 'graph_2',
445
- status: 'shared',
446
- error: null,
447
- fact_count: 150,
448
- },
449
- {
450
- target_graph_id: 'graph_3',
451
- status: 'error',
452
- error: 'Graph not found',
453
- fact_count: 0,
454
- },
455
- ],
456
- })
457
- )
458
-
459
- const result = await client.share('graph_1', 'rpt_1', 'pub_list_1')
460
-
461
- expect(result.reportId).toBe('rpt_1')
462
- expect(result.results).toHaveLength(2)
463
- expect(result.results[0]).toEqual({
464
- targetGraphId: 'graph_2',
465
- status: 'shared',
466
- error: null,
467
- factCount: 150,
468
- })
469
- expect(result.results[1]).toEqual({
470
- targetGraphId: 'graph_3',
471
- status: 'error',
472
- error: 'Graph not found',
473
- factCount: 0,
474
- })
475
- })
476
-
477
- it('should throw on error', async () => {
478
- mockFetch.mockResolvedValueOnce(
479
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
480
- )
481
-
482
- await expect(client.share('graph_1', 'rpt_1', 'pub_1')).rejects.toThrow('Share report failed')
483
- })
484
- })
485
-
486
- // ── isSharedReport ────────────────────────────────────────────────────
487
-
488
- describe('isSharedReport', () => {
489
- it('should return true for shared reports', () => {
490
- const report: Report = {
491
- id: 'rpt_1',
492
- name: 'Shared Report',
493
- taxonomyId: 'tax',
494
- generationStatus: 'completed',
495
- periodType: 'quarterly',
496
- periodStart: '2025-01-01',
497
- periodEnd: '2025-03-31',
498
- comparative: true,
499
- mappingId: 'map_1',
500
- aiGenerated: false,
501
- createdAt: '2025-04-01T00:00:00Z',
502
- lastGenerated: null,
503
- structures: [],
504
- entityName: 'Other Corp',
505
- sourceGraphId: 'other_graph',
506
- sourceReportId: 'rpt_orig',
507
- sharedAt: '2025-04-05T00:00:00Z',
508
- }
509
-
510
- expect(client.isSharedReport(report)).toBe(true)
511
- })
512
-
513
- it('should return false for native reports', () => {
514
- const report: Report = {
515
- id: 'rpt_1',
516
- name: 'Native Report',
517
- taxonomyId: 'tax',
518
- generationStatus: 'completed',
519
- periodType: 'quarterly',
520
- periodStart: '2025-01-01',
521
- periodEnd: '2025-03-31',
522
- comparative: true,
523
- mappingId: 'map_1',
524
- aiGenerated: false,
525
- createdAt: '2025-04-01T00:00:00Z',
526
- lastGenerated: null,
527
- structures: [],
528
- entityName: 'ACME Corp',
529
- sourceGraphId: null,
530
- sourceReportId: null,
531
- sharedAt: null,
532
- }
533
-
534
- expect(client.isSharedReport(report)).toBe(false)
535
- })
536
- })
537
-
538
- // ── Publish Lists ─────────────────────────────────────────────────────
539
-
540
- describe('listPublishLists', () => {
541
- it('should return transformed publish list', async () => {
542
- mockFetch.mockResolvedValueOnce(
543
- createMockResponse({
544
- publish_lists: [
545
- {
546
- id: 'pl_1',
547
- name: 'Investors',
548
- description: 'Investor distribution list',
549
- member_count: 5,
550
- created_by: 'user_1',
551
- created_at: '2025-01-01T00:00:00Z',
552
- updated_at: '2025-03-01T00:00:00Z',
553
- },
554
- ],
555
- })
556
- )
557
-
558
- const result = await client.listPublishLists('graph_1')
559
-
560
- expect(result).toHaveLength(1)
561
- expect(result[0]).toEqual({
562
- id: 'pl_1',
563
- name: 'Investors',
564
- description: 'Investor distribution list',
565
- memberCount: 5,
566
- createdBy: 'user_1',
567
- createdAt: '2025-01-01T00:00:00Z',
568
- updatedAt: '2025-03-01T00:00:00Z',
569
- })
570
- })
571
-
572
- it('should return empty array', async () => {
573
- mockFetch.mockResolvedValueOnce(createMockResponse({ publish_lists: [] }))
574
-
575
- const result = await client.listPublishLists('graph_1')
576
-
577
- expect(result).toEqual([])
578
- })
579
-
580
- it('should throw on error', async () => {
581
- mockFetch.mockResolvedValueOnce(
582
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
583
- )
584
-
585
- await expect(client.listPublishLists('graph_1')).rejects.toThrow('List publish lists failed')
586
- })
587
- })
588
-
589
- describe('createPublishList', () => {
590
- it('should create and return publish list', async () => {
591
- mockFetch.mockResolvedValueOnce(
592
- createMockResponse({
593
- id: 'pl_new',
594
- name: 'Board Members',
595
- description: 'Board distribution',
596
- member_count: 0,
597
- created_by: 'user_1',
598
- created_at: '2025-04-10T00:00:00Z',
599
- updated_at: '2025-04-10T00:00:00Z',
600
- })
601
- )
602
-
603
- const result = await client.createPublishList(
604
- 'graph_1',
605
- 'Board Members',
606
- 'Board distribution'
607
- )
608
-
609
- expect(result.id).toBe('pl_new')
610
- expect(result.name).toBe('Board Members')
611
- expect(result.description).toBe('Board distribution')
612
- })
613
-
614
- it('should handle null description', async () => {
615
- mockFetch.mockResolvedValueOnce(
616
- createMockResponse({
617
- id: 'pl_2',
618
- name: 'Minimal',
619
- member_count: 0,
620
- created_by: 'user_1',
621
- created_at: '2025-04-10T00:00:00Z',
622
- updated_at: '2025-04-10T00:00:00Z',
623
- })
624
- )
625
-
626
- const result = await client.createPublishList('graph_1', 'Minimal')
627
-
628
- expect(result.description).toBeNull()
629
- })
630
-
631
- it('should throw on error', async () => {
632
- mockFetch.mockResolvedValueOnce(
633
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
634
- )
635
-
636
- await expect(client.createPublishList('graph_1', 'Test')).rejects.toThrow(
637
- 'Create publish list failed'
638
- )
639
- })
640
- })
641
-
642
- describe('getPublishList', () => {
643
- it('should return publish list with members', async () => {
644
- mockFetch.mockResolvedValueOnce(
645
- createMockResponse({
646
- id: 'pl_1',
647
- name: 'Investors',
648
- description: 'List',
649
- member_count: 2,
650
- created_by: 'user_1',
651
- created_at: '2025-01-01T00:00:00Z',
652
- updated_at: '2025-03-01T00:00:00Z',
653
- members: [
654
- {
655
- id: 'mem_1',
656
- target_graph_id: 'graph_2',
657
- target_graph_name: 'Investor A',
658
- target_org_name: 'Org A',
659
- added_by: 'user_1',
660
- added_at: '2025-02-01T00:00:00Z',
661
- },
662
- {
663
- id: 'mem_2',
664
- target_graph_id: 'graph_3',
665
- target_graph_name: null,
666
- target_org_name: null,
667
- added_by: 'user_1',
668
- added_at: '2025-02-15T00:00:00Z',
669
- },
670
- ],
671
- })
672
- )
673
-
674
- const result = await client.getPublishList('graph_1', 'pl_1')
675
-
676
- expect(result.id).toBe('pl_1')
677
- expect(result.members).toHaveLength(2)
678
- expect(result.members[0]).toEqual({
679
- id: 'mem_1',
680
- targetGraphId: 'graph_2',
681
- targetGraphName: 'Investor A',
682
- targetOrgName: 'Org A',
683
- addedBy: 'user_1',
684
- addedAt: '2025-02-01T00:00:00Z',
685
- })
686
- expect(result.members[1].targetGraphName).toBeNull()
687
- expect(result.members[1].targetOrgName).toBeNull()
688
- })
689
-
690
- it('should throw on error', async () => {
691
- mockFetch.mockResolvedValueOnce(
692
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
693
- )
694
-
695
- await expect(client.getPublishList('graph_1', 'pl_1')).rejects.toThrow(
696
- 'Get publish list failed'
697
- )
698
- })
699
- })
700
-
701
- describe('updatePublishList', () => {
702
- it('should update and return publish list', async () => {
703
- mockFetch.mockResolvedValueOnce(
704
- createMockResponse({
705
- id: 'pl_1',
706
- name: 'Updated Name',
707
- description: 'Updated description',
708
- member_count: 3,
709
- created_by: 'user_1',
710
- created_at: '2025-01-01T00:00:00Z',
711
- updated_at: '2025-04-10T00:00:00Z',
712
- })
713
- )
714
-
715
- const result = await client.updatePublishList('graph_1', 'pl_1', {
716
- name: 'Updated Name',
717
- description: 'Updated description',
718
- })
719
-
720
- expect(result.name).toBe('Updated Name')
721
- expect(result.description).toBe('Updated description')
722
- })
723
-
724
- it('should throw on error', async () => {
725
- mockFetch.mockResolvedValueOnce(
726
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
727
- )
728
-
729
- await expect(client.updatePublishList('graph_1', 'pl_1', { name: 'X' })).rejects.toThrow(
730
- 'Update publish list failed'
731
- )
732
- })
733
- })
734
-
735
- describe('deletePublishList', () => {
736
- it('should delete publish list', async () => {
737
- mockFetch.mockResolvedValueOnce(createMockResponse({ success: true }))
738
-
739
- await expect(client.deletePublishList('graph_1', 'pl_1')).resolves.toBeUndefined()
740
- })
741
-
742
- it('should throw on error', async () => {
743
- mockFetch.mockResolvedValueOnce(
744
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
745
- )
746
-
747
- await expect(client.deletePublishList('graph_1', 'pl_1')).rejects.toThrow(
748
- 'Delete publish list failed'
749
- )
750
- })
751
- })
752
-
753
- describe('addMembers', () => {
754
- it('should add members and return them', async () => {
755
- mockFetch.mockResolvedValueOnce(
756
- createMockResponse([
757
- {
758
- id: 'mem_new_1',
759
- target_graph_id: 'graph_4',
760
- target_graph_name: 'New Investor',
761
- target_org_name: 'New Org',
762
- added_by: 'user_1',
763
- added_at: '2025-04-10T00:00:00Z',
764
- },
765
- ])
766
- )
767
-
768
- const result = await client.addMembers('graph_1', 'pl_1', ['graph_4'])
769
-
770
- expect(result).toHaveLength(1)
771
- expect(result[0]).toEqual({
772
- id: 'mem_new_1',
773
- targetGraphId: 'graph_4',
774
- targetGraphName: 'New Investor',
775
- targetOrgName: 'New Org',
776
- addedBy: 'user_1',
777
- addedAt: '2025-04-10T00:00:00Z',
778
- })
779
- })
780
-
781
- it('should throw on error', async () => {
782
- mockFetch.mockResolvedValueOnce(
783
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
784
- )
785
-
786
- await expect(client.addMembers('graph_1', 'pl_1', ['graph_4'])).rejects.toThrow(
787
- 'Add members failed'
788
- )
789
- })
790
- })
791
-
792
- describe('removeMember', () => {
793
- it('should remove member', async () => {
794
- mockFetch.mockResolvedValueOnce(createMockResponse({ success: true }))
795
-
796
- await expect(client.removeMember('graph_1', 'pl_1', 'mem_1')).resolves.toBeUndefined()
797
- })
798
-
799
- it('should throw on error', async () => {
800
- mockFetch.mockResolvedValueOnce(
801
- createMockResponse({ detail: 'Error' }, { ok: false, status: 500 })
802
- )
803
-
804
- await expect(client.removeMember('graph_1', 'pl_1', 'mem_1')).rejects.toThrow(
805
- 'Remove member failed'
806
- )
807
- })
808
- })
809
-
810
- // ── Constructor ───────────────────────────────────────────────────────
811
-
812
- describe('constructor', () => {
813
- it('should create with minimal config', () => {
814
- const c = new ReportClient({ baseUrl: 'http://localhost:8000' })
815
- expect(c).toBeInstanceOf(ReportClient)
816
- })
817
-
818
- it('should accept all config options', () => {
819
- const c = new ReportClient({
820
- baseUrl: 'http://localhost:8000',
821
- credentials: 'include',
822
- headers: { Authorization: 'Bearer token' },
823
- token: 'jwt-token',
824
- })
825
- expect(c).toBeInstanceOf(ReportClient)
826
- })
827
- })
828
- })