@robosystems/client 0.2.49 → 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 +183 -309
  5. package/extensions/LedgerClient.js +268 -587
  6. package/extensions/LedgerClient.ts +777 -903
  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 -73
  134. package/sdk/index.ts +2 -2
  135. package/sdk/sdk.gen.d.ts +100 -325
  136. package/sdk/sdk.gen.js +228 -592
  137. package/sdk/sdk.gen.ts +193 -556
  138. package/sdk/types.gen.d.ts +4522 -7708
  139. package/sdk/types.gen.ts +4569 -7983
  140. package/sdk.gen.d.ts +100 -325
  141. package/sdk.gen.js +228 -592
  142. package/sdk.gen.ts +193 -556
  143. package/types.gen.d.ts +4522 -7708
  144. package/types.gen.ts +4569 -7983
  145. package/extensions/AgentClient.test.ts +0 -403
  146. package/extensions/LedgerClient.test.ts +0 -1655
  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 -416
  159. package/sdk-extensions/LedgerClient.js +0 -737
  160. package/sdk-extensions/LedgerClient.test.ts +0 -1655
  161. package/sdk-extensions/LedgerClient.ts +0 -1249
  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
@@ -3,174 +3,259 @@
3
3
  /**
4
4
  * Ledger Client for RoboSystems API
5
5
  *
6
- * High-level client for all ledger concerns: chart of accounts, transactions,
7
- * trial balance, taxonomy, mappings, and AI auto-mapping. This is the
8
- * operational backbone reports consume these as inputs.
6
+ * High-level facade for everything the RoboLedger domain exposes:
7
+ * entity, chart of accounts, transactions, taxonomy + mappings, fiscal
8
+ * calendar, schedules, reports, and publish lists.
9
+ *
10
+ * **Transport split:**
11
+ * - **Reads** go through GraphQL at `/extensions/{graph_id}/graphql`
12
+ * (via `graphql-request`, with typed documents produced by GraphQL
13
+ * Code Generator). The graph is in the URL, not in the query.
14
+ * - **Writes** go through named command operations at
15
+ * `/extensions/roboledger/{graph_id}/operations/{operation_name}`
16
+ * (via the OpenAPI-generated `opXxx` functions in `../sdk/sdk.gen`).
17
+ * Each command returns an `OperationEnvelope`; the facade unwraps
18
+ * `envelope.result` and returns a friendly camelCase type.
19
+ *
20
+ * Consumers don't need to know which transport a method uses — the
21
+ * facade signature stays stable. The only trick is that write method
22
+ * results are cast from the envelope's untyped `result` field.
9
23
  */
10
24
 
25
+ import type { TypedDocumentNode } from '@graphql-typed-document-node/core'
26
+ import { ClientError } from 'graphql-request'
11
27
  import {
12
- autoMapElements,
13
- closeFiscalPeriod,
14
- createClosingEntry,
15
- createManualClosingEntry,
16
- createMappingAssociation,
17
- createSchedule,
18
- createStructure,
19
- deleteMappingAssociation,
20
- getAccountRollups,
21
- getClosingBookStructures,
22
- getFiscalCalendar,
23
- getLedgerAccountTree,
24
- getLedgerEntity,
25
- getLedgerSummary,
26
- getLedgerTransaction,
27
- getLedgerTrialBalance,
28
- getMappedTrialBalance,
29
- getMappingCoverage,
30
- getMappingDetail,
31
- getPeriodCloseStatus,
32
- getReportingTaxonomy,
33
- getScheduleFacts,
34
- initializeLedger,
35
- listElements,
36
- listLedgerAccounts,
37
- listLedgerTransactions,
38
- listMappings,
39
- listPeriodDrafts,
40
- listSchedules,
41
- listStructures,
42
- reopenFiscalPeriod,
43
- setCloseTarget,
44
- truncateSchedule,
28
+ opAutoMapElements,
29
+ opClosePeriod,
30
+ opCreateClosingEntry,
31
+ opCreateManualClosingEntry,
32
+ opCreateMappingAssociation,
33
+ opCreateSchedule,
34
+ opCreateStructure,
35
+ opCreateTaxonomy,
36
+ opDeleteMappingAssociation,
37
+ opInitializeLedger,
38
+ opReopenPeriod,
39
+ opSetCloseTarget,
40
+ opTruncateSchedule,
41
+ opUpdateEntity,
45
42
  } from '../sdk.gen'
46
43
  import type {
47
- AccountListResponse,
48
- AccountRollupsResponse,
49
- AccountTreeResponse,
50
- ClosePeriodRequest,
51
- ClosePeriodResponse,
52
- ClosingBookStructuresResponse,
53
- ClosingEntryResponse,
54
- CreateClosingEntryRequest,
44
+ AutoMapElementsOperation,
45
+ ClosePeriodOperation,
46
+ CreateClosingEntryOperation,
55
47
  CreateManualClosingEntryRequest,
48
+ CreateMappingAssociationOperation,
56
49
  CreateScheduleRequest,
57
- FiscalCalendarResponse,
50
+ CreateStructureRequest,
51
+ CreateTaxonomyRequest,
52
+ DeleteMappingAssociationOperation,
58
53
  InitializeLedgerRequest,
59
- InitializeLedgerResponse,
60
- LedgerSummaryResponse,
61
- LedgerTransactionDetailResponse,
62
- LedgerTransactionListResponse,
63
- MappingCoverageResponse,
64
- MappingDetailResponse,
65
- PeriodCloseItemResponse,
66
- PeriodCloseStatusResponse,
67
- PeriodDraftsResponse,
68
- ReopenPeriodRequest,
69
- ScheduleCreatedResponse,
70
- ScheduleFactResponse,
71
- ScheduleFactsResponse,
72
- ScheduleListResponse,
73
- ScheduleSummaryResponse,
74
- SetCloseTargetRequest,
75
- TrialBalanceResponse,
76
- TruncateScheduleRequest,
77
- TruncateScheduleResponse,
54
+ OperationEnvelope,
55
+ ReopenPeriodOperation,
56
+ SetCloseTargetOperation,
57
+ TruncateScheduleOperation,
58
+ UpdateEntityRequest,
78
59
  } from '../types.gen'
79
-
80
- // ── Friendly types ──────────────────────────────────────────────────────
81
-
82
- export interface LedgerEntity {
83
- id: string
84
- name: string
85
- legalName: string | null
86
- entityType: string | null
87
- industry: string | null
88
- status: string | null
60
+ import { GraphQLClientCache } from './graphql/client'
61
+ import {
62
+ GetLedgerAccountRollupsDocument,
63
+ GetLedgerAccountTreeDocument,
64
+ GetLedgerClosingBookStructuresDocument,
65
+ GetLedgerEntityDocument,
66
+ GetLedgerFiscalCalendarDocument,
67
+ GetLedgerMappedTrialBalanceDocument,
68
+ GetLedgerMappingCoverageDocument,
69
+ GetLedgerMappingDocument,
70
+ GetLedgerPeriodCloseStatusDocument,
71
+ GetLedgerPeriodDraftsDocument,
72
+ GetLedgerReportingTaxonomyDocument,
73
+ GetLedgerScheduleFactsDocument,
74
+ GetLedgerSummaryDocument,
75
+ GetLedgerTransactionDocument,
76
+ GetLedgerTrialBalanceDocument,
77
+ ListLedgerAccountsDocument,
78
+ ListLedgerElementsDocument,
79
+ ListLedgerEntitiesDocument,
80
+ ListLedgerMappingsDocument,
81
+ ListLedgerSchedulesDocument,
82
+ ListLedgerStructuresDocument,
83
+ ListLedgerTaxonomiesDocument,
84
+ ListLedgerTransactionsDocument,
85
+ ListLedgerUnmappedElementsDocument,
86
+ type GetLedgerAccountRollupsQuery,
87
+ type GetLedgerAccountTreeQuery,
88
+ type GetLedgerClosingBookStructuresQuery,
89
+ type GetLedgerEntityQuery,
90
+ type GetLedgerFiscalCalendarQuery,
91
+ type GetLedgerMappedTrialBalanceQuery,
92
+ type GetLedgerMappingCoverageQuery,
93
+ type GetLedgerMappingQuery,
94
+ type GetLedgerPeriodCloseStatusQuery,
95
+ type GetLedgerPeriodDraftsQuery,
96
+ type GetLedgerReportingTaxonomyQuery,
97
+ type GetLedgerScheduleFactsQuery,
98
+ type GetLedgerSummaryQuery,
99
+ type GetLedgerTransactionQuery,
100
+ type GetLedgerTrialBalanceQuery,
101
+ type ListLedgerAccountsQuery,
102
+ type ListLedgerElementsQuery,
103
+ type ListLedgerEntitiesQuery,
104
+ type ListLedgerMappingsQuery,
105
+ type ListLedgerSchedulesQuery,
106
+ type ListLedgerStructuresQuery,
107
+ type ListLedgerTaxonomiesQuery,
108
+ type ListLedgerTransactionsQuery,
109
+ type ListLedgerUnmappedElementsQuery,
110
+ } from './graphql/generated/graphql'
111
+
112
+ // ── Friendly types derived from GraphQL codegen ────────────────────────
113
+ //
114
+ // These are the single source of truth for read payload shapes. Write
115
+ // methods also return these where the operation result is semantically
116
+ // the same thing (e.g. close-period returns the updated fiscal calendar,
117
+ // the same shape as the fiscalCalendar read).
118
+
119
+ export type LedgerEntity = NonNullable<GetLedgerEntityQuery['entity']>
120
+ export type LedgerEntitySummary = ListLedgerEntitiesQuery['entities'][number]
121
+
122
+ export type LedgerSummary = NonNullable<GetLedgerSummaryQuery['summary']>
123
+
124
+ export type LedgerAccountList = NonNullable<ListLedgerAccountsQuery['accounts']>
125
+ export type LedgerAccount = LedgerAccountList['accounts'][number]
126
+ export type LedgerAccountTree = NonNullable<GetLedgerAccountTreeQuery['accountTree']>
127
+ export type LedgerAccountRollups = NonNullable<GetLedgerAccountRollupsQuery['accountRollups']>
128
+
129
+ export type LedgerTrialBalance = NonNullable<GetLedgerTrialBalanceQuery['trialBalance']>
130
+ export type LedgerMappedTrialBalance = NonNullable<
131
+ GetLedgerMappedTrialBalanceQuery['mappedTrialBalance']
132
+ >
133
+
134
+ export type LedgerTransactionList = NonNullable<ListLedgerTransactionsQuery['transactions']>
135
+ export type LedgerTransactionListItem = LedgerTransactionList['transactions'][number]
136
+ export type LedgerTransaction = NonNullable<GetLedgerTransactionQuery['transaction']>
137
+
138
+ export type LedgerReportingTaxonomy = NonNullable<
139
+ GetLedgerReportingTaxonomyQuery['reportingTaxonomy']
140
+ >
141
+ export type LedgerTaxonomyList = NonNullable<ListLedgerTaxonomiesQuery['taxonomies']>
142
+ export type LedgerTaxonomy = LedgerTaxonomyList['taxonomies'][number]
143
+
144
+ export type LedgerElementList = NonNullable<ListLedgerElementsQuery['elements']>
145
+ export type LedgerElement = LedgerElementList['elements'][number]
146
+ export type LedgerUnmappedElement = ListLedgerUnmappedElementsQuery['unmappedElements'][number]
147
+
148
+ export type LedgerStructureList = NonNullable<ListLedgerStructuresQuery['structures']>
149
+ export type LedgerStructure = LedgerStructureList['structures'][number]
150
+
151
+ export type LedgerMappingList = NonNullable<ListLedgerMappingsQuery['mappings']>
152
+ export type LedgerMappingInfo = LedgerMappingList['structures'][number]
153
+ export type LedgerMapping = NonNullable<GetLedgerMappingQuery['mapping']>
154
+ export type LedgerMappingCoverage = NonNullable<GetLedgerMappingCoverageQuery['mappingCoverage']>
155
+
156
+ export type LedgerScheduleList = NonNullable<ListLedgerSchedulesQuery['schedules']>
157
+ export type LedgerSchedule = LedgerScheduleList['schedules'][number]
158
+ export type LedgerScheduleFacts = NonNullable<GetLedgerScheduleFactsQuery['scheduleFacts']>
159
+ export type LedgerScheduleFact = LedgerScheduleFacts['facts'][number]
160
+
161
+ export type LedgerPeriodCloseStatus = NonNullable<
162
+ GetLedgerPeriodCloseStatusQuery['periodCloseStatus']
163
+ >
164
+ export type LedgerPeriodCloseItem = LedgerPeriodCloseStatus['schedules'][number]
165
+ export type LedgerPeriodDrafts = NonNullable<GetLedgerPeriodDraftsQuery['periodDrafts']>
166
+ export type LedgerDraftEntry = LedgerPeriodDrafts['drafts'][number]
167
+ export type LedgerDraftLineItem = LedgerDraftEntry['lineItems'][number]
168
+
169
+ export type LedgerClosingBookStructures = NonNullable<
170
+ GetLedgerClosingBookStructuresQuery['closingBookStructures']
171
+ >
172
+
173
+ export type LedgerFiscalCalendar = NonNullable<GetLedgerFiscalCalendarQuery['fiscalCalendar']>
174
+ export type LedgerFiscalPeriod = LedgerFiscalCalendar['periods'][number]
175
+
176
+ // ── Write result shapes (envelope.result payloads) ─────────────────────
177
+ //
178
+ // Backend Pydantic models serialize these write results in snake_case.
179
+ // The facade converts to camelCase where the result is meaningful to
180
+ // consumers; for simple ack/ack-with-id payloads we keep the raw shape.
181
+
182
+ /** Snake-case shape returned in envelope.result for fiscal calendar writes. */
183
+ interface RawFiscalCalendar {
184
+ graph_id: string
185
+ fiscal_year_start_month: number
186
+ closed_through: string | null
187
+ close_target: string | null
188
+ gap_periods: number
189
+ catch_up_sequence: string[]
190
+ closeable_now: boolean
191
+ blockers: string[]
192
+ last_close_at: string | null
193
+ initialized_at: string | null
194
+ last_sync_at: string | null
195
+ periods: Array<{
196
+ name: string
197
+ start_date: string
198
+ end_date: string
199
+ status: string
200
+ closed_at: string | null
201
+ }>
89
202
  }
90
203
 
91
- export interface MappingInfo {
92
- id: string
93
- name: string
94
- description: string | null
95
- structureType: string
96
- taxonomyId: string
97
- isActive: boolean
204
+ interface RawInitializeLedgerResult {
205
+ fiscal_calendar: RawFiscalCalendar
206
+ periods_created: number
207
+ warnings: string[]
98
208
  }
99
209
 
100
- export interface MappingCoverage {
101
- totalCoaElements: number
102
- mappedCount: number
103
- unmappedCount: number
104
- coveragePercent: number
105
- highConfidence: number
106
- mediumConfidence: number
107
- lowConfidence: number
210
+ interface RawClosePeriodResult {
211
+ period: string
212
+ entries_posted: number
213
+ target_auto_advanced: boolean
214
+ fiscal_calendar: RawFiscalCalendar
108
215
  }
109
216
 
110
- export interface Structure {
111
- id: string
112
- name: string
113
- structureType: string
217
+ interface RawClosingEntryResult {
218
+ outcome: ClosingEntryOutcome
219
+ entry_id: string | null
220
+ status: string | null
221
+ posting_date: string | null
222
+ memo: string | null
223
+ debit_element_id: string | null
224
+ credit_element_id: string | null
225
+ amount: number | null
226
+ reason: string | null
114
227
  }
115
228
 
116
- export interface Schedule {
117
- structureId: string
229
+ interface RawScheduleCreatedResult {
230
+ structure_id: string
118
231
  name: string
119
- taxonomyName: string
120
- entryTemplate: Record<string, unknown> | null
121
- scheduleMetadata: Record<string, unknown> | null
122
- totalPeriods: number
123
- periodsWithEntries: number
232
+ taxonomy_id: string
233
+ total_periods: number
234
+ total_facts: number
124
235
  }
125
236
 
126
- export interface ScheduleCreated {
127
- structureId: string
128
- name: string
129
- taxonomyId: string
130
- totalPeriods: number
131
- totalFacts: number
132
- }
133
-
134
- export interface ScheduleFact {
135
- elementId: string
136
- elementName: string
137
- value: number
138
- periodStart: string
139
- periodEnd: string
237
+ interface RawTruncateScheduleResult {
238
+ structure_id: string
239
+ new_end_date: string
240
+ facts_deleted: number
241
+ reason: string
140
242
  }
141
243
 
142
- export interface PeriodCloseItem {
143
- structureId: string
144
- structureName: string
145
- amount: number
146
- status: string
147
- entryId: string | null
244
+ export interface InitializeLedgerResult {
245
+ fiscalCalendar: LedgerFiscalCalendar
246
+ periodsCreated: number
247
+ warnings: string[]
148
248
  }
149
249
 
150
- export interface PeriodCloseStatus {
151
- fiscalPeriodStart: string
152
- fiscalPeriodEnd: string
153
- periodStatus: string
154
- schedules: PeriodCloseItem[]
155
- totalDraft: number
156
- totalPosted: number
250
+ export interface ClosePeriodResult {
251
+ period: string
252
+ entriesPosted: number
253
+ targetAutoAdvanced: boolean
254
+ fiscalCalendar: LedgerFiscalCalendar
157
255
  }
158
256
 
159
- /**
160
- * Outcome of an idempotent `createClosingEntry` call.
161
- *
162
- * - `created` — no prior draft, new draft created
163
- * - `unchanged` — prior draft matches current schedule fact, no-op
164
- * - `regenerated` — prior draft was stale, replaced with a fresh one
165
- * - `removed` — prior draft existed but schedule no longer covers this period
166
- * - `skipped` — no prior draft and no in-scope fact; nothing to do
167
- */
168
257
  export type ClosingEntryOutcome = 'created' | 'unchanged' | 'regenerated' | 'removed' | 'skipped'
169
258
 
170
- /**
171
- * Result of an idempotent closing-entry call. `entry_id`, `amount`, and
172
- * related fields are null for `removed` and `skipped` outcomes.
173
- */
174
259
  export interface ClosingEntry {
175
260
  outcome: ClosingEntryOutcome
176
261
  entryId: string | null
@@ -183,33 +268,25 @@ export interface ClosingEntry {
183
268
  reason: string | null
184
269
  }
185
270
 
186
- export type LedgerEntryType = 'standard' | 'adjusting' | 'closing' | 'reversing'
187
-
188
- // ── Fiscal calendar types ──────────────────────────────────────────────
189
-
190
- export interface FiscalPeriodSummary {
271
+ export interface ScheduleCreated {
272
+ structureId: string
191
273
  name: string
192
- startDate: string
193
- endDate: string
194
- status: string
195
- closedAt: string | null
274
+ taxonomyId: string
275
+ totalPeriods: number
276
+ totalFacts: number
196
277
  }
197
278
 
198
- export interface FiscalCalendarState {
199
- graphId: string
200
- fiscalYearStartMonth: number
201
- closedThrough: string | null
202
- closeTarget: string | null
203
- gapPeriods: number
204
- catchUpSequence: string[]
205
- closeableNow: boolean
206
- blockers: string[]
207
- lastCloseAt: string | null
208
- initializedAt: string | null
209
- lastSyncAt: string | null
210
- periods: FiscalPeriodSummary[]
279
+ export interface TruncateScheduleResult {
280
+ structureId: string
281
+ newEndDate: string
282
+ factsDeleted: number
283
+ reason: string
211
284
  }
212
285
 
286
+ export type LedgerEntryType = 'standard' | 'adjusting' | 'closing' | 'reversing'
287
+
288
+ // ── Caller-facing option interfaces ────────────────────────────────────
289
+
213
290
  export interface InitializeLedgerOptions {
214
291
  closedThrough?: string | null
215
292
  fiscalYearStartMonth?: number
@@ -218,59 +295,11 @@ export interface InitializeLedgerOptions {
218
295
  note?: string | null
219
296
  }
220
297
 
221
- export interface InitializeLedgerResult {
222
- fiscalCalendar: FiscalCalendarState
223
- periodsCreated: number
224
- warnings: string[]
225
- }
226
-
227
298
  export interface ClosePeriodOptions {
228
299
  note?: string | null
229
300
  allowStaleSync?: boolean
230
301
  }
231
302
 
232
- export interface ClosePeriodResult {
233
- period: string
234
- entriesPosted: number
235
- targetAutoAdvanced: boolean
236
- fiscalCalendar: FiscalCalendarState
237
- }
238
-
239
- export interface DraftLineItemView {
240
- lineItemId: string
241
- elementId: string
242
- elementCode: string | null
243
- elementName: string
244
- debitAmount: number
245
- creditAmount: number
246
- description: string | null
247
- }
248
-
249
- export interface DraftEntryView {
250
- entryId: string
251
- postingDate: string
252
- type: string
253
- memo: string | null
254
- provenance: string | null
255
- sourceStructureId: string | null
256
- sourceStructureName: string | null
257
- lineItems: DraftLineItemView[]
258
- totalDebit: number
259
- totalCredit: number
260
- balanced: boolean
261
- }
262
-
263
- export interface PeriodDraftsView {
264
- period: string
265
- periodStart: string
266
- periodEnd: string
267
- draftCount: number
268
- totalDebit: number
269
- totalCredit: number
270
- allBalanced: boolean
271
- drafts: DraftEntryView[]
272
- }
273
-
274
303
  export interface ManualClosingLineItem {
275
304
  elementId: string
276
305
  debitAmount?: number
@@ -290,13 +319,6 @@ export interface TruncateScheduleOptions {
290
319
  reason: string
291
320
  }
292
321
 
293
- export interface TruncateScheduleResult {
294
- structureId: string
295
- newEndDate: string
296
- factsDeleted: number
297
- reason: string
298
- }
299
-
300
322
  export interface CreateScheduleOptions {
301
323
  name: string
302
324
  elementIds: string[]
@@ -321,230 +343,259 @@ export interface CreateScheduleOptions {
321
343
 
322
344
  // ── Client ──────────────────────────────────────────────────────────────
323
345
 
346
+ interface LedgerClientConfig {
347
+ baseUrl: string
348
+ credentials?: 'include' | 'same-origin' | 'omit'
349
+ headers?: Record<string, string>
350
+ token?: string
351
+ }
352
+
324
353
  export class LedgerClient {
325
- private config: {
326
- baseUrl: string
327
- credentials?: 'include' | 'same-origin' | 'omit'
328
- headers?: Record<string, string>
329
- token?: string
330
- }
354
+ private config: LedgerClientConfig
355
+
356
+ /**
357
+ * Per-graph GraphQL client cache. The first call for a given graph
358
+ * creates a `GraphQLClient` bound to `/extensions/{graph_id}/graphql`;
359
+ * subsequent calls reuse it.
360
+ */
361
+ private gql: GraphQLClientCache
331
362
 
332
- constructor(config: {
333
- baseUrl: string
334
- credentials?: 'include' | 'same-origin' | 'omit'
335
- headers?: Record<string, string>
336
- token?: string
337
- }) {
363
+ constructor(config: LedgerClientConfig) {
338
364
  this.config = config
365
+ this.gql = new GraphQLClientCache(config)
339
366
  }
340
367
 
341
368
  // ── Entity ──────────────────────────────────────────────────────────
342
369
 
343
370
  /**
344
371
  * Get the entity (company/organization) for this graph.
372
+ * Returns null when the ledger has no entity yet.
345
373
  */
346
374
  async getEntity(graphId: string): Promise<LedgerEntity | null> {
347
- const response = await getLedgerEntity({
348
- path: { graph_id: graphId },
349
- })
350
-
351
- if (response.response.status === 404) return null
352
- if (response.error) {
353
- throw new Error(`Get entity failed: ${JSON.stringify(response.error)}`)
354
- }
355
-
356
- const data = response.data as Record<string, unknown>
357
- return {
358
- id: data.id as string,
359
- name: data.name as string,
360
- legalName: (data.legal_name as string) ?? null,
361
- entityType: (data.entity_type as string) ?? null,
362
- industry: (data.industry as string) ?? null,
363
- status: (data.status as string) ?? null,
364
- }
375
+ return this.gqlQuery(
376
+ graphId,
377
+ GetLedgerEntityDocument,
378
+ undefined,
379
+ 'Get entity',
380
+ (data) => data.entity
381
+ )
365
382
  }
366
383
 
367
- // ── Accounts (Chart of Accounts) ───────────────────────────────────
384
+ /** List all entities for this graph, optionally filtered by source system. */
385
+ async listEntities(
386
+ graphId: string,
387
+ options?: { source?: string }
388
+ ): Promise<LedgerEntitySummary[]> {
389
+ return this.gqlQuery(
390
+ graphId,
391
+ ListLedgerEntitiesDocument,
392
+ { source: options?.source ?? null },
393
+ 'List entities',
394
+ (data) => data.entities
395
+ )
396
+ }
368
397
 
369
398
  /**
370
- * List accounts (flat).
399
+ * Update the entity for this graph. Only non-null fields are applied.
400
+ * Returns the updated entity.
371
401
  */
372
- async listAccounts(graphId: string): Promise<AccountListResponse> {
373
- const response = await listLedgerAccounts({
374
- path: { graph_id: graphId },
375
- })
402
+ async updateEntity(graphId: string, updates: UpdateEntityRequest): Promise<LedgerEntity> {
403
+ const envelope = await this.callOperation(
404
+ 'Update entity',
405
+ opUpdateEntity({
406
+ path: { graph_id: graphId },
407
+ body: updates,
408
+ })
409
+ )
410
+ return envelope.result as unknown as LedgerEntity
411
+ }
376
412
 
377
- if (response.error) {
378
- throw new Error(`List accounts failed: ${JSON.stringify(response.error)}`)
379
- }
413
+ // ── Summary ────────────────────────────────────────────────────────
380
414
 
381
- return response.data as AccountListResponse
415
+ /** Ledger rollup counts + QB sync metadata. */
416
+ async getSummary(graphId: string): Promise<LedgerSummary | null> {
417
+ return this.gqlQuery(
418
+ graphId,
419
+ GetLedgerSummaryDocument,
420
+ undefined,
421
+ 'Get summary',
422
+ (data) => data.summary
423
+ )
382
424
  }
383
425
 
384
- /**
385
- * Get the account tree (hierarchical).
386
- */
387
- async getAccountTree(graphId: string): Promise<AccountTreeResponse> {
388
- const response = await getLedgerAccountTree({
389
- path: { graph_id: graphId },
390
- })
426
+ // ── Accounts (Chart of Accounts) ───────────────────────────────────
391
427
 
392
- if (response.error) {
393
- throw new Error(`Get account tree failed: ${JSON.stringify(response.error)}`)
428
+ /** List CoA accounts with optional filters and pagination. */
429
+ async listAccounts(
430
+ graphId: string,
431
+ options?: {
432
+ classification?: string
433
+ isActive?: boolean
434
+ limit?: number
435
+ offset?: number
394
436
  }
395
-
396
- return response.data as AccountTreeResponse
437
+ ): Promise<LedgerAccountList | null> {
438
+ return this.gqlQuery(
439
+ graphId,
440
+ ListLedgerAccountsDocument,
441
+ {
442
+ classification: options?.classification ?? null,
443
+ isActive: options?.isActive ?? null,
444
+ limit: options?.limit ?? 100,
445
+ offset: options?.offset ?? 0,
446
+ },
447
+ 'List accounts',
448
+ (data) => data.accounts
449
+ )
397
450
  }
398
451
 
399
- // ── Transactions ────────────────────────────────────────────────────
452
+ /** Hierarchical Chart of Accounts (up to 4 levels deep). */
453
+ async getAccountTree(graphId: string): Promise<LedgerAccountTree | null> {
454
+ return this.gqlQuery(
455
+ graphId,
456
+ GetLedgerAccountTreeDocument,
457
+ undefined,
458
+ 'Get account tree',
459
+ (data) => data.accountTree
460
+ )
461
+ }
400
462
 
401
- /**
402
- * List transactions with optional date filters.
403
- */
404
- async listTransactions(
463
+ /** Accounts rolled up to reporting concepts via a mapping structure. */
464
+ async getAccountRollups(
405
465
  graphId: string,
406
- options?: { startDate?: string; endDate?: string; limit?: number; offset?: number }
407
- ): Promise<LedgerTransactionListResponse> {
408
- const response = await listLedgerTransactions({
409
- path: { graph_id: graphId },
410
- query: {
411
- start_date: options?.startDate,
412
- end_date: options?.endDate,
413
- limit: options?.limit,
414
- offset: options?.offset,
466
+ options?: { mappingId?: string; startDate?: string; endDate?: string }
467
+ ): Promise<LedgerAccountRollups | null> {
468
+ return this.gqlQuery(
469
+ graphId,
470
+ GetLedgerAccountRollupsDocument,
471
+ {
472
+ mappingId: options?.mappingId ?? null,
473
+ startDate: options?.startDate ?? null,
474
+ endDate: options?.endDate ?? null,
415
475
  },
416
- })
417
-
418
- if (response.error) {
419
- throw new Error(`List transactions failed: ${JSON.stringify(response.error)}`)
420
- }
421
-
422
- return response.data as LedgerTransactionListResponse
476
+ 'Get account rollups',
477
+ (data) => data.accountRollups
478
+ )
423
479
  }
424
480
 
425
- /**
426
- * Get transaction detail with entries and line items.
427
- */
428
- async getTransaction(
481
+ // ── Transactions ────────────────────────────────────────────────────
482
+
483
+ /** List transactions with optional type + date filters and pagination. */
484
+ async listTransactions(
429
485
  graphId: string,
430
- transactionId: string
431
- ): Promise<LedgerTransactionDetailResponse> {
432
- const response = await getLedgerTransaction({
433
- path: { graph_id: graphId, transaction_id: transactionId },
434
- })
435
-
436
- if (response.error) {
437
- throw new Error(`Get transaction failed: ${JSON.stringify(response.error)}`)
486
+ options?: {
487
+ type?: string
488
+ startDate?: string
489
+ endDate?: string
490
+ limit?: number
491
+ offset?: number
438
492
  }
493
+ ): Promise<LedgerTransactionList | null> {
494
+ return this.gqlQuery(
495
+ graphId,
496
+ ListLedgerTransactionsDocument,
497
+ {
498
+ type: options?.type ?? null,
499
+ startDate: options?.startDate ?? null,
500
+ endDate: options?.endDate ?? null,
501
+ limit: options?.limit ?? 100,
502
+ offset: options?.offset ?? 0,
503
+ },
504
+ 'List transactions',
505
+ (data) => data.transactions
506
+ )
507
+ }
439
508
 
440
- return response.data as LedgerTransactionDetailResponse
509
+ /** Get transaction detail with entries + line items. */
510
+ async getTransaction(graphId: string, transactionId: string): Promise<LedgerTransaction | null> {
511
+ return this.gqlQuery(
512
+ graphId,
513
+ GetLedgerTransactionDocument,
514
+ { transactionId },
515
+ 'Get transaction',
516
+ (data) => data.transaction
517
+ )
441
518
  }
442
519
 
443
520
  // ── Trial Balance ──────────────────────────────────────────────────
444
521
 
445
- /**
446
- * Get trial balance (CoA-level debits/credits).
447
- */
522
+ /** Trial balance by raw CoA account. */
448
523
  async getTrialBalance(
449
524
  graphId: string,
450
525
  options?: { startDate?: string; endDate?: string }
451
- ): Promise<TrialBalanceResponse> {
452
- const response = await getLedgerTrialBalance({
453
- path: { graph_id: graphId },
454
- query: {
455
- start_date: options?.startDate,
456
- end_date: options?.endDate,
526
+ ): Promise<LedgerTrialBalance | null> {
527
+ return this.gqlQuery(
528
+ graphId,
529
+ GetLedgerTrialBalanceDocument,
530
+ {
531
+ startDate: options?.startDate ?? null,
532
+ endDate: options?.endDate ?? null,
457
533
  },
458
- })
459
-
460
- if (response.error) {
461
- throw new Error(`Get trial balance failed: ${JSON.stringify(response.error)}`)
462
- }
463
-
464
- return response.data as TrialBalanceResponse
534
+ 'Get trial balance',
535
+ (data) => data.trialBalance
536
+ )
465
537
  }
466
538
 
467
- /**
468
- * Get mapped trial balance (CoA rolled up to GAAP concepts).
469
- */
539
+ /** Trial balance rolled up to GAAP reporting concepts via a mapping. */
470
540
  async getMappedTrialBalance(
471
541
  graphId: string,
472
- options?: { mappingId?: string; startDate?: string; endDate?: string }
473
- ): Promise<unknown> {
474
- const response = await getMappedTrialBalance({
475
- path: { graph_id: graphId },
476
- query: {
477
- mapping_id: options?.mappingId,
478
- start_date: options?.startDate,
479
- end_date: options?.endDate,
542
+ mappingId: string,
543
+ options?: { startDate?: string; endDate?: string }
544
+ ): Promise<LedgerMappedTrialBalance | null> {
545
+ return this.gqlQuery(
546
+ graphId,
547
+ GetLedgerMappedTrialBalanceDocument,
548
+ {
549
+ mappingId,
550
+ startDate: options?.startDate ?? null,
551
+ endDate: options?.endDate ?? null,
480
552
  },
481
- })
482
-
483
- if (response.error) {
484
- throw new Error(`Get mapped trial balance failed: ${JSON.stringify(response.error)}`)
485
- }
486
-
487
- return response.data
488
- }
489
-
490
- // ── Summary ────────────────────────────────────────────────────────
491
-
492
- /**
493
- * Get ledger summary (account count, transaction count, date range).
494
- */
495
- async getSummary(graphId: string): Promise<LedgerSummaryResponse> {
496
- const response = await getLedgerSummary({
497
- path: { graph_id: graphId },
498
- })
499
-
500
- if (response.error) {
501
- throw new Error(`Get summary failed: ${JSON.stringify(response.error)}`)
502
- }
503
-
504
- return response.data as LedgerSummaryResponse
553
+ 'Get mapped trial balance',
554
+ (data) => data.mappedTrialBalance
555
+ )
505
556
  }
506
557
 
507
558
  // ── Taxonomy ────────────────────────────────────────────────────────
508
559
 
509
- /**
510
- * Get the reporting taxonomy (US GAAP seed).
511
- */
512
- async getReportingTaxonomy(graphId: string): Promise<unknown> {
513
- const response = await getReportingTaxonomy({
514
- path: { graph_id: graphId },
515
- })
516
-
517
- if (response.error) {
518
- throw new Error(`Get reporting taxonomy failed: ${JSON.stringify(response.error)}`)
519
- }
520
-
521
- return response.data
560
+ /** Get the locked US GAAP reporting taxonomy for this graph. */
561
+ async getReportingTaxonomy(graphId: string): Promise<LedgerReportingTaxonomy | null> {
562
+ return this.gqlQuery(
563
+ graphId,
564
+ GetLedgerReportingTaxonomyDocument,
565
+ undefined,
566
+ 'Get reporting taxonomy',
567
+ (data) => data.reportingTaxonomy
568
+ )
522
569
  }
523
570
 
524
- /**
525
- * List reporting structures (IS, BS, CF) for a taxonomy.
526
- */
527
- async listStructures(graphId: string, taxonomyId?: string): Promise<Structure[]> {
528
- const response = await listStructures({
529
- path: { graph_id: graphId },
530
- query: taxonomyId ? { taxonomy_id: taxonomyId } : undefined,
531
- })
532
-
533
- if (response.error) {
534
- throw new Error(`List structures failed: ${JSON.stringify(response.error)}`)
535
- }
571
+ /** List active taxonomies with optional type filter. */
572
+ async listTaxonomies(
573
+ graphId: string,
574
+ options?: { taxonomyType?: string }
575
+ ): Promise<LedgerTaxonomy[]> {
576
+ const list = await this.gqlQuery(
577
+ graphId,
578
+ ListLedgerTaxonomiesDocument,
579
+ { taxonomyType: options?.taxonomyType ?? null },
580
+ 'List taxonomies',
581
+ (data) => data.taxonomies
582
+ )
583
+ return list?.taxonomies ?? []
584
+ }
536
585
 
537
- const data = response.data as { structures?: Array<Record<string, unknown>> }
538
- return (data.structures ?? []).map((s) => ({
539
- id: s.id as string,
540
- name: s.name as string,
541
- structureType: s.structure_type as string,
542
- }))
586
+ /** Create a new taxonomy (used for CoA + mapping taxonomies). */
587
+ async createTaxonomy(
588
+ graphId: string,
589
+ body: CreateTaxonomyRequest
590
+ ): Promise<Record<string, unknown>> {
591
+ const envelope = await this.callOperation(
592
+ 'Create taxonomy',
593
+ opCreateTaxonomy({ path: { graph_id: graphId }, body })
594
+ )
595
+ return (envelope.result ?? {}) as Record<string, unknown>
543
596
  }
544
597
 
545
- /**
546
- * List elements (CoA accounts, GAAP concepts, etc.).
547
- */
598
+ /** List elements (CoA accounts, GAAP concepts, etc.) with filters. */
548
599
  async listElements(
549
600
  graphId: string,
550
601
  options?: {
@@ -555,184 +606,183 @@ export class LedgerClient {
555
606
  limit?: number
556
607
  offset?: number
557
608
  }
558
- ): Promise<unknown> {
559
- const response = await listElements({
560
- path: { graph_id: graphId },
561
- query: {
562
- taxonomy_id: options?.taxonomyId,
563
- source: options?.source,
564
- classification: options?.classification,
565
- is_abstract: options?.isAbstract,
566
- limit: options?.limit,
567
- offset: options?.offset,
609
+ ): Promise<LedgerElementList | null> {
610
+ return this.gqlQuery(
611
+ graphId,
612
+ ListLedgerElementsDocument,
613
+ {
614
+ taxonomyId: options?.taxonomyId ?? null,
615
+ source: options?.source ?? null,
616
+ classification: options?.classification ?? null,
617
+ isAbstract: options?.isAbstract ?? null,
618
+ limit: options?.limit ?? 100,
619
+ offset: options?.offset ?? 0,
568
620
  },
569
- })
570
-
571
- if (response.error) {
572
- throw new Error(`List elements failed: ${JSON.stringify(response.error)}`)
573
- }
621
+ 'List elements',
622
+ (data) => data.elements
623
+ )
624
+ }
574
625
 
575
- return response.data
626
+ /** CoA elements not yet mapped to a reporting concept. */
627
+ async listUnmappedElements(
628
+ graphId: string,
629
+ options?: { mappingId?: string }
630
+ ): Promise<LedgerUnmappedElement[]> {
631
+ return this.gqlQuery(
632
+ graphId,
633
+ ListLedgerUnmappedElementsDocument,
634
+ { mappingId: options?.mappingId ?? null },
635
+ 'List unmapped elements',
636
+ (data) => data.unmappedElements
637
+ )
576
638
  }
577
639
 
578
- // ── Mappings ────────────────────────────────────────────────────────
640
+ // ── Structures ──────────────────────────────────────────────────────
579
641
 
580
- /**
581
- * Create a new CoA→GAAP mapping structure.
582
- * Returns the created mapping info.
583
- */
584
- async createMappingStructure(
642
+ /** List reporting structures (IS, BS, CF, schedules) with optional filters. */
643
+ async listStructures(
585
644
  graphId: string,
586
- options?: { name?: string; description?: string; taxonomyId?: string }
587
- ): Promise<MappingInfo> {
588
- const response = await createStructure({
589
- path: { graph_id: graphId },
590
- body: {
591
- name: options?.name ?? 'CoA to Reporting',
592
- description: options?.description ?? 'Map Chart of Accounts to US GAAP reporting concepts',
593
- structure_type: 'coa_mapping',
594
- taxonomy_id: options?.taxonomyId ?? 'tax_usgaap_reporting',
645
+ options?: { taxonomyId?: string; structureType?: string }
646
+ ): Promise<LedgerStructure[]> {
647
+ const list = await this.gqlQuery(
648
+ graphId,
649
+ ListLedgerStructuresDocument,
650
+ {
651
+ taxonomyId: options?.taxonomyId ?? null,
652
+ structureType: options?.structureType ?? null,
595
653
  },
596
- })
597
-
598
- if (response.error) {
599
- throw new Error(`Create mapping structure failed: ${JSON.stringify(response.error)}`)
600
- }
601
-
602
- const data = response.data as Record<string, unknown>
603
- return {
604
- id: data.id as string,
605
- name: data.name as string,
606
- description: (data.description as string) ?? null,
607
- structureType: data.structure_type as string,
608
- taxonomyId: data.taxonomy_id as string,
609
- isActive: (data.is_active as boolean) ?? true,
610
- }
654
+ 'List structures',
655
+ (data) => data.structures
656
+ )
657
+ return list?.structures ?? []
611
658
  }
612
659
 
613
660
  /**
614
- * List available CoA→GAAP mapping structures.
661
+ * Create a new structure. Most common use is a CoA→reporting mapping
662
+ * structure; also used for custom statement + schedule structures.
615
663
  */
616
- async listMappings(graphId: string): Promise<MappingInfo[]> {
617
- const response = await listMappings({
618
- path: { graph_id: graphId },
619
- })
620
-
621
- if (response.error) {
622
- throw new Error(`List mappings failed: ${JSON.stringify(response.error)}`)
623
- }
624
-
625
- const data = response.data as { structures?: Array<Record<string, unknown>> }
626
- return (data.structures ?? []).map((s) => ({
627
- id: s.id as string,
628
- name: s.name as string,
629
- description: (s.description as string) ?? null,
630
- structureType: s.structure_type as string,
631
- taxonomyId: s.taxonomy_id as string,
632
- isActive: (s.is_active as boolean) ?? true,
633
- }))
664
+ async createStructure(graphId: string, body: CreateStructureRequest): Promise<LedgerMappingInfo> {
665
+ const envelope = await this.callOperation(
666
+ 'Create structure',
667
+ opCreateStructure({ path: { graph_id: graphId }, body })
668
+ )
669
+ return envelope.result as unknown as LedgerMappingInfo
634
670
  }
635
671
 
636
- /**
637
- * Get mapping detail — all associations with element names.
638
- */
639
- async getMappingDetail(graphId: string, mappingId: string): Promise<MappingDetailResponse> {
640
- const response = await getMappingDetail({
641
- path: { graph_id: graphId, mapping_id: mappingId },
642
- })
643
-
644
- if (response.error) {
645
- throw new Error(`Get mapping detail failed: ${JSON.stringify(response.error)}`)
646
- }
672
+ // ── Mappings ────────────────────────────────────────────────────────
647
673
 
648
- return response.data as MappingDetailResponse
674
+ /** List active CoA→reporting mapping structures. */
675
+ async listMappings(graphId: string): Promise<LedgerMappingInfo[]> {
676
+ const list = await this.gqlQuery(
677
+ graphId,
678
+ ListLedgerMappingsDocument,
679
+ undefined,
680
+ 'List mappings',
681
+ (data) => data.mappings
682
+ )
683
+ return list?.structures ?? []
649
684
  }
650
685
 
651
- /**
652
- * Get mapping coverage how many CoA elements are mapped.
653
- */
654
- async getMappingCoverage(graphId: string, mappingId: string): Promise<MappingCoverage> {
655
- const response = await getMappingCoverage({
656
- path: { graph_id: graphId, mapping_id: mappingId },
657
- })
658
-
659
- if (response.error) {
660
- throw new Error(`Get mapping coverage failed: ${JSON.stringify(response.error)}`)
661
- }
662
-
663
- const data = response.data as MappingCoverageResponse
664
- return {
665
- totalCoaElements: data.total_coa_elements ?? 0,
666
- mappedCount: data.mapped_count ?? 0,
667
- unmappedCount: data.unmapped_count ?? 0,
668
- coveragePercent: data.coverage_percent ?? 0,
669
- highConfidence: data.high_confidence ?? 0,
670
- mediumConfidence: data.medium_confidence ?? 0,
671
- lowConfidence: data.low_confidence ?? 0,
672
- }
686
+ /** Get a mapping structure with all its associations. */
687
+ async getMapping(graphId: string, mappingId: string): Promise<LedgerMapping | null> {
688
+ return this.gqlQuery(
689
+ graphId,
690
+ GetLedgerMappingDocument,
691
+ { mappingId },
692
+ 'Get mapping',
693
+ (data) => data.mapping
694
+ )
673
695
  }
674
696
 
675
- /**
676
- * Create a manual mapping association (CoA element → GAAP element).
677
- */
678
- async createMapping(
697
+ /** Mapping coverage stats — how many CoA elements are mapped. */
698
+ async getMappingCoverage(
679
699
  graphId: string,
680
- mappingId: string,
681
- fromElementId: string,
682
- toElementId: string,
683
- confidence?: number
684
- ): Promise<void> {
685
- const response = await createMappingAssociation({
686
- path: { graph_id: graphId, mapping_id: mappingId },
687
- body: {
688
- from_element_id: fromElementId,
689
- to_element_id: toElementId,
690
- confidence: confidence ?? 1.0,
691
- },
692
- })
693
-
694
- if (response.error) {
695
- throw new Error(`Create mapping failed: ${JSON.stringify(response.error)}`)
696
- }
700
+ mappingId: string
701
+ ): Promise<LedgerMappingCoverage | null> {
702
+ return this.gqlQuery(
703
+ graphId,
704
+ GetLedgerMappingCoverageDocument,
705
+ { mappingId },
706
+ 'Get mapping coverage',
707
+ (data) => data.mappingCoverage
708
+ )
697
709
  }
698
710
 
699
- /**
700
- * Delete a mapping association.
701
- */
702
- async deleteMapping(graphId: string, mappingId: string, associationId: string): Promise<void> {
703
- const response = await deleteMappingAssociation({
704
- path: { graph_id: graphId, mapping_id: mappingId, association_id: associationId },
705
- })
711
+ /** Create a manual mapping association between two elements. */
712
+ async createMappingAssociation(
713
+ graphId: string,
714
+ body: CreateMappingAssociationOperation
715
+ ): Promise<Record<string, unknown>> {
716
+ const envelope = await this.callOperation(
717
+ 'Create mapping association',
718
+ opCreateMappingAssociation({ path: { graph_id: graphId }, body })
719
+ )
720
+ return (envelope.result ?? {}) as Record<string, unknown>
721
+ }
706
722
 
707
- if (response.error) {
708
- throw new Error(`Delete mapping failed: ${JSON.stringify(response.error)}`)
709
- }
723
+ /** Delete a mapping association. */
724
+ async deleteMappingAssociation(
725
+ graphId: string,
726
+ body: DeleteMappingAssociationOperation
727
+ ): Promise<{ deleted: boolean }> {
728
+ const envelope = await this.callOperation(
729
+ 'Delete mapping association',
730
+ opDeleteMappingAssociation({ path: { graph_id: graphId }, body })
731
+ )
732
+ return (envelope.result ?? { deleted: true }) as { deleted: boolean }
710
733
  }
711
734
 
712
735
  /**
713
- * Trigger AI auto-mapping (MappingAgent).
714
- * Returns immediately — the agent runs in the background.
736
+ * Trigger the AI MappingAgent. Returns the operation id — async; the
737
+ * agent runs in the background. Consumers can subscribe to progress
738
+ * via `/v1/operations/{operationId}/stream`.
715
739
  */
716
- async autoMap(graphId: string, mappingId: string): Promise<{ operationId?: string }> {
717
- const response = await autoMapElements({
718
- path: { graph_id: graphId, mapping_id: mappingId },
719
- })
740
+ async autoMapElements(
741
+ graphId: string,
742
+ body: AutoMapElementsOperation
743
+ ): Promise<{ operationId: string; status: OperationEnvelope['status'] }> {
744
+ const envelope = await this.callOperation(
745
+ 'Auto-map elements',
746
+ opAutoMapElements({ path: { graph_id: graphId }, body })
747
+ )
748
+ return { operationId: envelope.operationId, status: envelope.status }
749
+ }
720
750
 
721
- if (response.error) {
722
- throw new Error(`Auto-map failed: ${JSON.stringify(response.error)}`)
723
- }
751
+ // ── Schedules ──────────────────────────────────────────────────────
724
752
 
725
- const data = response.data as Record<string, unknown> | undefined
726
- return {
727
- operationId: data?.operation_id as string | undefined,
728
- }
753
+ /** List all schedule structures with metadata. */
754
+ async listSchedules(graphId: string): Promise<LedgerSchedule[]> {
755
+ const list = await this.gqlQuery(
756
+ graphId,
757
+ ListLedgerSchedulesDocument,
758
+ undefined,
759
+ 'List schedules',
760
+ (data) => data.schedules
761
+ )
762
+ return list?.schedules ?? []
729
763
  }
730
764
 
731
- // ── Schedules ──────────────────────────────────────────────────────
765
+ /** Schedule facts optionally filtered by period window. */
766
+ async getScheduleFacts(
767
+ graphId: string,
768
+ structureId: string,
769
+ options?: { periodStart?: string; periodEnd?: string }
770
+ ): Promise<LedgerScheduleFact[]> {
771
+ const facts = await this.gqlQuery(
772
+ graphId,
773
+ GetLedgerScheduleFactsDocument,
774
+ {
775
+ structureId,
776
+ periodStart: options?.periodStart ?? null,
777
+ periodEnd: options?.periodEnd ?? null,
778
+ },
779
+ 'Get schedule facts',
780
+ (data) => data.scheduleFacts
781
+ )
782
+ return facts?.facts ?? []
783
+ }
732
784
 
733
- /**
734
- * Create a schedule with pre-generated monthly facts.
735
- */
785
+ /** Create a new schedule with pre-generated monthly facts. */
736
786
  async createSchedule(graphId: string, options: CreateScheduleOptions): Promise<ScheduleCreated> {
737
787
  const body: CreateScheduleRequest = {
738
788
  name: options.name,
@@ -757,124 +807,75 @@ export class LedgerClient {
757
807
  }
758
808
  : undefined,
759
809
  }
760
-
761
- const response = await createSchedule({
762
- path: { graph_id: graphId },
763
- body,
764
- })
765
-
766
- if (response.error) {
767
- throw new Error(`Create schedule failed: ${JSON.stringify(response.error)}`)
768
- }
769
-
770
- const data = response.data as ScheduleCreatedResponse
810
+ const envelope = await this.callOperation(
811
+ 'Create schedule',
812
+ opCreateSchedule({ path: { graph_id: graphId }, body })
813
+ )
814
+ const raw = envelope.result as unknown as RawScheduleCreatedResult
771
815
  return {
772
- structureId: data.structure_id,
773
- name: data.name,
774
- taxonomyId: data.taxonomy_id,
775
- totalPeriods: data.total_periods,
776
- totalFacts: data.total_facts,
777
- }
778
- }
779
-
780
- /**
781
- * List all schedules for a graph.
782
- */
783
- async listSchedules(graphId: string): Promise<Schedule[]> {
784
- const response = await listSchedules({
785
- path: { graph_id: graphId },
786
- })
787
-
788
- if (response.error) {
789
- throw new Error(`List schedules failed: ${JSON.stringify(response.error)}`)
816
+ structureId: raw.structure_id,
817
+ name: raw.name,
818
+ taxonomyId: raw.taxonomy_id,
819
+ totalPeriods: raw.total_periods,
820
+ totalFacts: raw.total_facts,
790
821
  }
791
-
792
- const data = response.data as ScheduleListResponse
793
- return (data.schedules ?? []).map((s: ScheduleSummaryResponse) => ({
794
- structureId: s.structure_id,
795
- name: s.name,
796
- taxonomyName: s.taxonomy_name,
797
- entryTemplate: s.entry_template ?? null,
798
- scheduleMetadata: s.schedule_metadata ?? null,
799
- totalPeriods: s.total_periods,
800
- periodsWithEntries: s.periods_with_entries,
801
- }))
802
822
  }
803
823
 
804
- /**
805
- * Get facts for a schedule, optionally filtered by period.
806
- */
807
- async getScheduleFacts(
824
+ /** Truncate a schedule — end it early at `newEndDate`. */
825
+ async truncateSchedule(
808
826
  graphId: string,
809
827
  structureId: string,
810
- periodStart?: string,
811
- periodEnd?: string
812
- ): Promise<ScheduleFact[]> {
813
- const response = await getScheduleFacts({
814
- path: { graph_id: graphId, structure_id: structureId },
815
- query: {
816
- period_start: periodStart ?? null,
817
- period_end: periodEnd ?? null,
818
- },
819
- })
820
-
821
- if (response.error) {
822
- throw new Error(`Get schedule facts failed: ${JSON.stringify(response.error)}`)
828
+ options: TruncateScheduleOptions
829
+ ): Promise<TruncateScheduleResult> {
830
+ const body: TruncateScheduleOperation = {
831
+ structure_id: structureId,
832
+ new_end_date: options.newEndDate,
833
+ reason: options.reason,
834
+ }
835
+ const envelope = await this.callOperation(
836
+ 'Truncate schedule',
837
+ opTruncateSchedule({ path: { graph_id: graphId }, body })
838
+ )
839
+ const raw = envelope.result as unknown as RawTruncateScheduleResult
840
+ return {
841
+ structureId: raw.structure_id,
842
+ newEndDate: raw.new_end_date,
843
+ factsDeleted: raw.facts_deleted,
844
+ reason: raw.reason,
823
845
  }
824
-
825
- const data = response.data as ScheduleFactsResponse
826
- return (data.facts ?? []).map((f: ScheduleFactResponse) => ({
827
- elementId: f.element_id,
828
- elementName: f.element_name,
829
- value: f.value,
830
- periodStart: f.period_start,
831
- periodEnd: f.period_end,
832
- }))
833
846
  }
834
847
 
835
- /**
836
- * Get close status for all schedules in a fiscal period.
837
- */
848
+ // ── Period close ────────────────────────────────────────────────────
849
+
850
+ /** Close status for all schedules in a fiscal period. */
838
851
  async getPeriodCloseStatus(
839
852
  graphId: string,
840
853
  periodStart: string,
841
854
  periodEnd: string
842
- ): Promise<PeriodCloseStatus> {
843
- const response = await getPeriodCloseStatus({
844
- path: { graph_id: graphId },
845
- query: { period_start: periodStart, period_end: periodEnd },
846
- })
847
-
848
- if (response.error) {
849
- throw new Error(`Get period close status failed: ${JSON.stringify(response.error)}`)
850
- }
855
+ ): Promise<LedgerPeriodCloseStatus | null> {
856
+ return this.gqlQuery(
857
+ graphId,
858
+ GetLedgerPeriodCloseStatusDocument,
859
+ { periodStart, periodEnd },
860
+ 'Get period close status',
861
+ (data) => data.periodCloseStatus
862
+ )
863
+ }
851
864
 
852
- const data = response.data as PeriodCloseStatusResponse
853
- return {
854
- fiscalPeriodStart: data.fiscal_period_start,
855
- fiscalPeriodEnd: data.fiscal_period_end,
856
- periodStatus: data.period_status,
857
- schedules: (data.schedules ?? []).map((s: PeriodCloseItemResponse) => ({
858
- structureId: s.structure_id,
859
- structureName: s.structure_name,
860
- amount: s.amount,
861
- status: s.status,
862
- entryId: s.entry_id ?? null,
863
- })),
864
- totalDraft: data.total_draft,
865
- totalPosted: data.total_posted,
866
- }
865
+ /** All draft entries in a period, fully expanded for review pre-close. */
866
+ async listPeriodDrafts(graphId: string, period: string): Promise<LedgerPeriodDrafts | null> {
867
+ return this.gqlQuery(
868
+ graphId,
869
+ GetLedgerPeriodDraftsDocument,
870
+ { period },
871
+ 'List period drafts',
872
+ (data) => data.periodDrafts
873
+ )
867
874
  }
868
875
 
869
876
  /**
870
- * Idempotently create (or refresh) a draft closing entry from a schedule's
871
- * facts for a period. The `outcome` field describes what actually happened:
872
- *
873
- * - `created` — new draft
874
- * - `unchanged` — existing draft still matches the schedule; no-op
875
- * - `regenerated` — existing draft was stale; replaced
876
- * - `removed` — schedule no longer covers this period; stale draft deleted
877
- * - `skipped` — no existing draft and no in-scope fact; nothing to do
877
+ * Idempotently create (or refresh) a draft closing entry from a
878
+ * schedule for a period. See `ClosingEntryOutcome` for semantics.
878
879
  */
879
880
  async createClosingEntry(
880
881
  graphId: string,
@@ -884,76 +885,73 @@ export class LedgerClient {
884
885
  periodEnd: string,
885
886
  memo?: string
886
887
  ): Promise<ClosingEntry> {
887
- const body: CreateClosingEntryRequest = {
888
+ const body: CreateClosingEntryOperation = {
889
+ structure_id: structureId,
888
890
  posting_date: postingDate,
889
891
  period_start: periodStart,
890
892
  period_end: periodEnd,
891
893
  memo: memo ?? undefined,
892
894
  }
893
-
894
- const response = await createClosingEntry({
895
- path: { graph_id: graphId, structure_id: structureId },
896
- body,
897
- })
898
-
899
- if (response.error) {
900
- throw new Error(`Create closing entry failed: ${JSON.stringify(response.error)}`)
901
- }
902
-
903
- const data = response.data as ClosingEntryResponse
904
- return toClosingEntry(data)
895
+ const envelope = await this.callOperation(
896
+ 'Create closing entry',
897
+ opCreateClosingEntry({ path: { graph_id: graphId }, body })
898
+ )
899
+ return rawToClosingEntry(envelope.result as unknown as RawClosingEntryResult)
905
900
  }
906
901
 
907
- // ── Closing Book ─────────────────────────────────────────────────────
908
-
909
902
  /**
910
- * Get all closing book structure categories for the sidebar.
911
- * Returns statements, account rollups, schedules, trial balance,
912
- * and period close grouped into categories.
903
+ * Create a manual balanced closing entry (not tied to a schedule).
904
+ * Used for disposals, adjustments, and one-off closing events.
913
905
  */
914
- async getClosingBookStructures(graphId: string): Promise<ClosingBookStructuresResponse> {
915
- const response = await getClosingBookStructures({
916
- path: { graph_id: graphId },
917
- })
918
-
919
- if (response.error) {
920
- throw new Error(`Get closing book structures failed: ${JSON.stringify(response.error)}`)
921
- }
922
-
923
- return response.data as ClosingBookStructuresResponse
924
- }
925
-
926
- /**
927
- * Get account rollups — CoA accounts grouped by reporting element with balances.
928
- * Shows how company-specific accounts roll up to standardized reporting lines.
929
- */
930
- async getAccountRollups(
906
+ async createManualClosingEntry(
931
907
  graphId: string,
932
- options?: { mappingId?: string; startDate?: string; endDate?: string }
933
- ): Promise<AccountRollupsResponse> {
934
- const response = await getAccountRollups({
935
- path: { graph_id: graphId },
936
- query: {
937
- mapping_id: options?.mappingId,
938
- start_date: options?.startDate,
939
- end_date: options?.endDate,
940
- },
941
- })
942
-
943
- if (response.error) {
944
- throw new Error(`Get account rollups failed: ${JSON.stringify(response.error)}`)
908
+ options: CreateManualClosingEntryOptions
909
+ ): Promise<ClosingEntry> {
910
+ const body: CreateManualClosingEntryRequest = {
911
+ posting_date: options.postingDate,
912
+ memo: options.memo,
913
+ entry_type: options.entryType,
914
+ line_items: options.lineItems.map((li) => ({
915
+ element_id: li.elementId,
916
+ debit_amount: li.debitAmount ?? 0,
917
+ credit_amount: li.creditAmount ?? 0,
918
+ description: li.description ?? null,
919
+ })),
945
920
  }
921
+ const envelope = await this.callOperation(
922
+ 'Create manual closing entry',
923
+ opCreateManualClosingEntry({ path: { graph_id: graphId }, body })
924
+ )
925
+ return rawToClosingEntry(envelope.result as unknown as RawClosingEntryResult)
926
+ }
946
927
 
947
- return response.data as AccountRollupsResponse
928
+ // ── Closing book ───────────────────────────────────────────────────
929
+
930
+ /** Grouped closing book structures for the close-screen sidebar. */
931
+ async getClosingBookStructures(graphId: string): Promise<LedgerClosingBookStructures | null> {
932
+ return this.gqlQuery(
933
+ graphId,
934
+ GetLedgerClosingBookStructuresDocument,
935
+ undefined,
936
+ 'Get closing book structures',
937
+ (data) => data.closingBookStructures
938
+ )
948
939
  }
949
940
 
950
- // ── Fiscal Calendar ─────────────────────────────────────────────────
941
+ // ── Fiscal Calendar ────────────────────────────────────────────────
942
+
943
+ /** Current fiscal calendar state — pointers, gap, closeable status. */
944
+ async getFiscalCalendar(graphId: string): Promise<LedgerFiscalCalendar | null> {
945
+ return this.gqlQuery(
946
+ graphId,
947
+ GetLedgerFiscalCalendarDocument,
948
+ undefined,
949
+ 'Get fiscal calendar',
950
+ (data) => data.fiscalCalendar
951
+ )
952
+ }
951
953
 
952
- /**
953
- * Initialize the fiscal calendar for a graph. Creates FiscalPeriod rows
954
- * for the data window, sets `closed_through` / `close_target`, and emits
955
- * an `initialized` audit event. Fails with 409 if already initialized.
956
- */
954
+ /** One-time ledger initialization — seed fiscal calendar + periods. */
957
955
  async initializeLedger(
958
956
  graphId: string,
959
957
  options?: InitializeLedgerOptions
@@ -965,255 +963,131 @@ export class LedgerClient {
965
963
  auto_seed_schedules: options?.autoSeedSchedules,
966
964
  note: options?.note ?? null,
967
965
  }
968
-
969
- const response = await initializeLedger({
970
- path: { graph_id: graphId },
971
- body,
972
- })
973
-
974
- if (response.error) {
975
- throw new Error(`Initialize ledger failed: ${JSON.stringify(response.error)}`)
976
- }
977
-
978
- const data = response.data as InitializeLedgerResponse
966
+ const envelope = await this.callOperation(
967
+ 'Initialize ledger',
968
+ opInitializeLedger({ path: { graph_id: graphId }, body })
969
+ )
970
+ const raw = envelope.result as unknown as RawInitializeLedgerResult
979
971
  return {
980
- fiscalCalendar: toFiscalCalendarState(data.fiscal_calendar),
981
- periodsCreated: data.periods_created ?? 0,
982
- warnings: data.warnings ?? [],
972
+ fiscalCalendar: rawFiscalCalendarToCamel(raw.fiscal_calendar),
973
+ periodsCreated: raw.periods_created ?? 0,
974
+ warnings: raw.warnings ?? [],
983
975
  }
984
976
  }
985
977
 
986
- /**
987
- * Get the current fiscal calendar state — pointers, gap, closeable status.
988
- */
989
- async getFiscalCalendar(graphId: string): Promise<FiscalCalendarState> {
990
- const response = await getFiscalCalendar({
991
- path: { graph_id: graphId },
992
- })
993
-
994
- if (response.error) {
995
- throw new Error(`Get fiscal calendar failed: ${JSON.stringify(response.error)}`)
996
- }
997
-
998
- return toFiscalCalendarState(response.data as FiscalCalendarResponse)
999
- }
1000
-
1001
- /**
1002
- * Set the close target for a graph. Validates that the target is not in
1003
- * the future and not before `closed_through`.
1004
- */
978
+ /** Set the user-controlled close target (YYYY-MM). */
1005
979
  async setCloseTarget(
1006
980
  graphId: string,
1007
981
  period: string,
1008
982
  note?: string | null
1009
- ): Promise<FiscalCalendarState> {
1010
- const body: SetCloseTargetRequest = {
1011
- period,
1012
- note: note ?? null,
1013
- }
1014
-
1015
- const response = await setCloseTarget({
1016
- path: { graph_id: graphId },
1017
- body,
1018
- })
1019
-
1020
- if (response.error) {
1021
- throw new Error(`Set close target failed: ${JSON.stringify(response.error)}`)
1022
- }
1023
-
1024
- return toFiscalCalendarState(response.data as FiscalCalendarResponse)
983
+ ): Promise<LedgerFiscalCalendar> {
984
+ const body: SetCloseTargetOperation = { period, note: note ?? null }
985
+ const envelope = await this.callOperation(
986
+ 'Set close target',
987
+ opSetCloseTarget({ path: { graph_id: graphId }, body })
988
+ )
989
+ return rawFiscalCalendarToCamel(envelope.result as unknown as RawFiscalCalendar)
1025
990
  }
1026
991
 
1027
- /**
1028
- * Close a fiscal period — the final commit action.
1029
- *
1030
- * Validates closeable gates, transitions all draft entries in the period
1031
- * to `posted`, marks the FiscalPeriod closed, and advances `closed_through`
1032
- * (auto-advancing `close_target` when reached).
1033
- */
992
+ /** Close a fiscal period — the final commit action. */
1034
993
  async closePeriod(
1035
994
  graphId: string,
1036
995
  period: string,
1037
996
  options?: ClosePeriodOptions
1038
997
  ): Promise<ClosePeriodResult> {
1039
- const body: ClosePeriodRequest = {
998
+ const body: ClosePeriodOperation = {
999
+ period,
1040
1000
  note: options?.note ?? null,
1041
1001
  allow_stale_sync: options?.allowStaleSync,
1042
1002
  }
1043
-
1044
- const response = await closeFiscalPeriod({
1045
- path: { graph_id: graphId, period },
1046
- body,
1047
- })
1048
-
1049
- if (response.error) {
1050
- throw new Error(`Close period failed: ${JSON.stringify(response.error)}`)
1051
- }
1052
-
1053
- const data = response.data as ClosePeriodResponse
1003
+ const envelope = await this.callOperation(
1004
+ 'Close period',
1005
+ opClosePeriod({ path: { graph_id: graphId }, body })
1006
+ )
1007
+ const raw = envelope.result as unknown as RawClosePeriodResult
1054
1008
  return {
1055
- period: data.period,
1056
- entriesPosted: data.entries_posted ?? 0,
1057
- targetAutoAdvanced: data.target_auto_advanced ?? false,
1058
- fiscalCalendar: toFiscalCalendarState(data.fiscal_calendar),
1009
+ period: raw.period,
1010
+ entriesPosted: raw.entries_posted ?? 0,
1011
+ targetAutoAdvanced: raw.target_auto_advanced ?? false,
1012
+ fiscalCalendar: rawFiscalCalendarToCamel(raw.fiscal_calendar),
1059
1013
  }
1060
1014
  }
1061
1015
 
1062
- /**
1063
- * Reopen a closed fiscal period. Requires a non-empty `reason` for the
1064
- * audit log. Posted entries stay posted; the period transitions to
1065
- * `closing` so the user can post adjustments and re-close.
1066
- */
1016
+ /** Reopen a closed fiscal period. Requires a reason for the audit log. */
1067
1017
  async reopenPeriod(
1068
1018
  graphId: string,
1069
1019
  period: string,
1070
1020
  reason: string,
1071
1021
  note?: string | null
1072
- ): Promise<FiscalCalendarState> {
1073
- const body: ReopenPeriodRequest = {
1022
+ ): Promise<LedgerFiscalCalendar> {
1023
+ const body: ReopenPeriodOperation = {
1024
+ period,
1074
1025
  reason,
1075
1026
  note: note ?? null,
1076
1027
  }
1077
-
1078
- const response = await reopenFiscalPeriod({
1079
- path: { graph_id: graphId, period },
1080
- body,
1081
- })
1082
-
1083
- if (response.error) {
1084
- throw new Error(`Reopen period failed: ${JSON.stringify(response.error)}`)
1085
- }
1086
-
1087
- return toFiscalCalendarState(response.data as FiscalCalendarResponse)
1028
+ const envelope = await this.callOperation(
1029
+ 'Reopen period',
1030
+ opReopenPeriod({ path: { graph_id: graphId }, body })
1031
+ )
1032
+ return rawFiscalCalendarToCamel(envelope.result as unknown as RawFiscalCalendar)
1088
1033
  }
1089
1034
 
1090
- /**
1091
- * List all draft entries in a fiscal period for review before close.
1092
- * Fully expanded with line items, element metadata, and per-entry balance.
1093
- *
1094
- * Pure read — call repeatedly without side effects.
1095
- */
1096
- async listPeriodDrafts(graphId: string, period: string): Promise<PeriodDraftsView> {
1097
- const response = await listPeriodDrafts({
1098
- path: { graph_id: graphId, period },
1099
- })
1035
+ // Reports, statements, and publish lists live on `ReportClient`.
1100
1036
 
1101
- if (response.error) {
1102
- throw new Error(`List period drafts failed: ${JSON.stringify(response.error)}`)
1103
- }
1104
-
1105
- const data = response.data as PeriodDraftsResponse
1106
- return {
1107
- period: data.period,
1108
- periodStart: data.period_start,
1109
- periodEnd: data.period_end,
1110
- draftCount: data.draft_count,
1111
- totalDebit: data.total_debit,
1112
- totalCredit: data.total_credit,
1113
- allBalanced: data.all_balanced,
1114
- drafts: (data.drafts ?? []).map((d) => ({
1115
- entryId: d.entry_id,
1116
- postingDate: d.posting_date,
1117
- type: d.type,
1118
- memo: d.memo ?? null,
1119
- provenance: d.provenance ?? null,
1120
- sourceStructureId: d.source_structure_id ?? null,
1121
- sourceStructureName: d.source_structure_name ?? null,
1122
- lineItems: d.line_items.map((li) => ({
1123
- lineItemId: li.line_item_id,
1124
- elementId: li.element_id,
1125
- elementCode: li.element_code ?? null,
1126
- elementName: li.element_name,
1127
- debitAmount: li.debit_amount,
1128
- creditAmount: li.credit_amount,
1129
- description: li.description ?? null,
1130
- })),
1131
- totalDebit: d.total_debit,
1132
- totalCredit: d.total_credit,
1133
- balanced: d.balanced,
1134
- })),
1135
- }
1136
- }
1137
-
1138
- // ── Schedule mutations ──────────────────────────────────────────────
1037
+ // ── Internal helpers ────────────────────────────────────────────────
1139
1038
 
1140
1039
  /**
1141
- * Truncate a schedule end it early by deleting facts with
1142
- * `period_start > new_end_date`, along with any stale draft entries they
1143
- * produced. Historical posted facts are preserved.
1144
- *
1145
- * `new_end_date` must be a month-end date (service enforces this).
1040
+ * Run a typed GraphQL query against the per-graph endpoint and
1041
+ * translate ClientError into a readable facade error.
1146
1042
  */
1147
- async truncateSchedule(
1043
+ private async gqlQuery<TData, TVars extends object, TResult>(
1148
1044
  graphId: string,
1149
- structureId: string,
1150
- options: TruncateScheduleOptions
1151
- ): Promise<TruncateScheduleResult> {
1152
- const body: TruncateScheduleRequest = {
1153
- new_end_date: options.newEndDate,
1154
- reason: options.reason,
1155
- }
1156
-
1157
- const response = await truncateSchedule({
1158
- path: { graph_id: graphId, structure_id: structureId },
1159
- body,
1160
- })
1161
-
1162
- if (response.error) {
1163
- throw new Error(`Truncate schedule failed: ${JSON.stringify(response.error)}`)
1164
- }
1165
-
1166
- const data = response.data as TruncateScheduleResponse
1167
- return {
1168
- structureId: data.structure_id,
1169
- newEndDate: data.new_end_date,
1170
- factsDeleted: data.facts_deleted,
1171
- reason: data.reason,
1045
+ document: TypedDocumentNode<TData, TVars>,
1046
+ variables: TVars | undefined,
1047
+ label: string,
1048
+ pick: (data: TData) => TResult
1049
+ ): Promise<TResult> {
1050
+ try {
1051
+ const client = this.gql.get(graphId)
1052
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1053
+ const raw = client.request as (doc: unknown, vars?: unknown) => Promise<any>
1054
+ // graphql-request's overloads don't cleanly resolve for generic
1055
+ // helpers wrapping codegen's `Exact<>` var types, so we bypass
1056
+ // the typed overload with a narrow cast of `request` itself.
1057
+ const data = (await raw.call(client, document, variables)) as TData
1058
+ return pick(data)
1059
+ } catch (err) {
1060
+ if (err instanceof ClientError) {
1061
+ throw new Error(`${label} failed: ${JSON.stringify(err.response.errors ?? err.message)}`)
1062
+ }
1063
+ throw err
1172
1064
  }
1173
1065
  }
1174
1066
 
1175
1067
  /**
1176
- * Create a manual draft closing entry with arbitrary balanced line items.
1177
- * Not tied to a schedule used for disposals, adjustments, and other
1178
- * one-off closing events.
1179
- *
1180
- * Line items must sum to balanced debits/credits. Rejects entries
1181
- * targeting an already-closed period.
1068
+ * Await an SDK-generated `opXxx(...)` call, throw a readable error on
1069
+ * non-2xx, and return the `OperationEnvelope` on success.
1182
1070
  */
1183
- async createManualClosingEntry(
1184
- graphId: string,
1185
- options: CreateManualClosingEntryOptions
1186
- ): Promise<ClosingEntry> {
1187
- const body: CreateManualClosingEntryRequest = {
1188
- posting_date: options.postingDate,
1189
- memo: options.memo,
1190
- entry_type: options.entryType,
1191
- line_items: options.lineItems.map((li) => ({
1192
- element_id: li.elementId,
1193
- debit_amount: li.debitAmount ?? 0,
1194
- credit_amount: li.creditAmount ?? 0,
1195
- description: li.description ?? null,
1196
- })),
1071
+ private async callOperation(
1072
+ label: string,
1073
+ call: Promise<{ data?: OperationEnvelope; error?: unknown }>
1074
+ ): Promise<OperationEnvelope> {
1075
+ const response = await call
1076
+ if (response.error !== undefined) {
1077
+ throw new Error(`${label} failed: ${JSON.stringify(response.error)}`)
1197
1078
  }
1198
-
1199
- const response = await createManualClosingEntry({
1200
- path: { graph_id: graphId },
1201
- body,
1202
- })
1203
-
1204
- if (response.error) {
1205
- throw new Error(`Create manual closing entry failed: ${JSON.stringify(response.error)}`)
1079
+ if (response.data === undefined) {
1080
+ throw new Error(`${label} failed: empty response`)
1206
1081
  }
1207
-
1208
- return toClosingEntry(response.data as ClosingEntryResponse)
1082
+ return response.data
1209
1083
  }
1210
1084
  }
1211
1085
 
1212
- // ── Internal helpers ──────────────────────────────────────────────────
1086
+ // ── Module-private conversion helpers ─────────────────────────────────
1213
1087
 
1214
- function toClosingEntry(data: ClosingEntryResponse): ClosingEntry {
1088
+ function rawToClosingEntry(data: RawClosingEntryResult): ClosingEntry {
1215
1089
  return {
1216
- outcome: data.outcome as ClosingEntryOutcome,
1090
+ outcome: data.outcome,
1217
1091
  entryId: data.entry_id ?? null,
1218
1092
  status: data.status ?? null,
1219
1093
  postingDate: data.posting_date ?? null,
@@ -1225,20 +1099,20 @@ function toClosingEntry(data: ClosingEntryResponse): ClosingEntry {
1225
1099
  }
1226
1100
  }
1227
1101
 
1228
- function toFiscalCalendarState(data: FiscalCalendarResponse): FiscalCalendarState {
1102
+ function rawFiscalCalendarToCamel(raw: RawFiscalCalendar): LedgerFiscalCalendar {
1229
1103
  return {
1230
- graphId: data.graph_id,
1231
- fiscalYearStartMonth: data.fiscal_year_start_month,
1232
- closedThrough: data.closed_through ?? null,
1233
- closeTarget: data.close_target ?? null,
1234
- gapPeriods: data.gap_periods ?? 0,
1235
- catchUpSequence: data.catch_up_sequence ?? [],
1236
- closeableNow: data.closeable_now ?? false,
1237
- blockers: data.blockers ?? [],
1238
- lastCloseAt: data.last_close_at ?? null,
1239
- initializedAt: data.initialized_at ?? null,
1240
- lastSyncAt: data.last_sync_at ?? null,
1241
- periods: (data.periods ?? []).map((p) => ({
1104
+ graphId: raw.graph_id,
1105
+ fiscalYearStartMonth: raw.fiscal_year_start_month,
1106
+ closedThrough: raw.closed_through ?? null,
1107
+ closeTarget: raw.close_target ?? null,
1108
+ gapPeriods: raw.gap_periods ?? 0,
1109
+ catchUpSequence: raw.catch_up_sequence ?? [],
1110
+ closeableNow: raw.closeable_now ?? false,
1111
+ blockers: raw.blockers ?? [],
1112
+ lastCloseAt: raw.last_close_at ?? null,
1113
+ initializedAt: raw.initialized_at ?? null,
1114
+ lastSyncAt: raw.last_sync_at ?? null,
1115
+ periods: (raw.periods ?? []).map((p) => ({
1242
1116
  name: p.name,
1243
1117
  startDate: p.start_date,
1244
1118
  endDate: p.end_date,