mcp-dataverse 0.1.5 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +1049 -992
- package/README.md +80 -78
- package/config.example.json +0 -1
- package/dist/auth/auth-provider.factory.d.ts +2 -2
- package/dist/auth/auth-provider.factory.js +4 -4
- package/dist/auth/msal-auth-provider.d.ts +2 -2
- package/dist/auth/msal-auth-provider.d.ts.map +1 -1
- package/dist/auth/msal-auth-provider.js +9 -7
- package/dist/auth/msal-auth-provider.js.map +1 -1
- package/dist/auth/pac-auth-provider.d.ts +1 -1
- package/dist/auth/pac-auth-provider.d.ts.map +1 -1
- package/dist/auth/pac-auth-provider.js +39 -35
- package/dist/auth/pac-auth-provider.js.map +1 -1
- package/dist/config/config.loader.d.ts +1 -1
- package/dist/config/config.loader.d.ts.map +1 -1
- package/dist/config/config.loader.js +22 -25
- package/dist/config/config.loader.js.map +1 -1
- package/dist/config/config.schema.d.ts +1 -4
- package/dist/config/config.schema.d.ts.map +1 -1
- package/dist/config/config.schema.js +11 -11
- package/dist/config/config.schema.js.map +1 -1
- package/dist/dataverse/dataverse-client-advanced.d.ts +1 -1
- package/dist/dataverse/dataverse-client-advanced.d.ts.map +1 -1
- package/dist/dataverse/dataverse-client-advanced.js +70 -39
- package/dist/dataverse/dataverse-client-advanced.js.map +1 -1
- package/dist/dataverse/dataverse-client.actions.d.ts +11 -0
- package/dist/dataverse/dataverse-client.actions.d.ts.map +1 -0
- package/dist/dataverse/dataverse-client.actions.js +25 -0
- package/dist/dataverse/dataverse-client.actions.js.map +1 -0
- package/dist/dataverse/dataverse-client.batch.d.ts +2 -2
- package/dist/dataverse/dataverse-client.batch.d.ts.map +1 -1
- package/dist/dataverse/dataverse-client.batch.js +12 -12
- package/dist/dataverse/dataverse-client.batch.js.map +1 -1
- package/dist/dataverse/dataverse-client.d.ts +5 -8
- package/dist/dataverse/dataverse-client.d.ts.map +1 -1
- package/dist/dataverse/dataverse-client.js +81 -81
- package/dist/dataverse/dataverse-client.js.map +1 -1
- package/dist/dataverse/dataverse-client.metadata.d.ts +3 -3
- package/dist/dataverse/dataverse-client.metadata.d.ts.map +1 -1
- package/dist/dataverse/dataverse-client.metadata.js +45 -45
- package/dist/dataverse/dataverse-client.metadata.js.map +1 -1
- package/dist/dataverse/dataverse-client.utils.d.ts.map +1 -1
- package/dist/dataverse/dataverse-client.utils.js +5 -5
- package/dist/dataverse/dataverse-client.utils.js.map +1 -1
- package/dist/dataverse/http-client.d.ts +1 -1
- package/dist/dataverse/http-client.d.ts.map +1 -1
- package/dist/dataverse/http-client.js +25 -18
- package/dist/dataverse/http-client.js.map +1 -1
- package/dist/dataverse/types.d.ts +5 -5
- package/dist/server.js +57 -57
- package/dist/server.js.map +1 -1
- package/dist/setup-auth.js +8 -8
- package/dist/setup-auth.js.map +1 -1
- package/dist/tools/actions.tools.d.ts +2 -2
- package/dist/tools/actions.tools.d.ts.map +1 -1
- package/dist/tools/actions.tools.js +108 -65
- package/dist/tools/actions.tools.js.map +1 -1
- package/dist/tools/annotations.tools.d.ts +2 -2
- package/dist/tools/annotations.tools.d.ts.map +1 -1
- package/dist/tools/annotations.tools.js +79 -70
- package/dist/tools/annotations.tools.js.map +1 -1
- package/dist/tools/audit.tools.d.ts +2 -2
- package/dist/tools/audit.tools.d.ts.map +1 -1
- package/dist/tools/audit.tools.js +51 -48
- package/dist/tools/audit.tools.js.map +1 -1
- package/dist/tools/auth.tools.d.ts +2 -2
- package/dist/tools/auth.tools.d.ts.map +1 -1
- package/dist/tools/auth.tools.js +9 -7
- package/dist/tools/auth.tools.js.map +1 -1
- package/dist/tools/batch.tools.d.ts +2 -2
- package/dist/tools/batch.tools.d.ts.map +1 -1
- package/dist/tools/batch.tools.js +39 -26
- package/dist/tools/batch.tools.js.map +1 -1
- package/dist/tools/crud.tools.d.ts +2 -2
- package/dist/tools/crud.tools.d.ts.map +1 -1
- package/dist/tools/crud.tools.js +140 -83
- package/dist/tools/crud.tools.js.map +1 -1
- package/dist/tools/customization.tools.d.ts +2 -2
- package/dist/tools/customization.tools.d.ts.map +1 -1
- package/dist/tools/customization.tools.js +72 -62
- package/dist/tools/customization.tools.js.map +1 -1
- package/dist/tools/environment.tools.d.ts +2 -2
- package/dist/tools/environment.tools.d.ts.map +1 -1
- package/dist/tools/environment.tools.js +62 -55
- package/dist/tools/environment.tools.js.map +1 -1
- package/dist/tools/file.tools.d.ts +2 -2
- package/dist/tools/file.tools.d.ts.map +1 -1
- package/dist/tools/file.tools.js +63 -44
- package/dist/tools/file.tools.js.map +1 -1
- package/dist/tools/impersonate.tools.d.ts +3 -3
- package/dist/tools/impersonate.tools.d.ts.map +1 -1
- package/dist/tools/impersonate.tools.js +22 -20
- package/dist/tools/impersonate.tools.js.map +1 -1
- package/dist/tools/metadata.tools.d.ts +2 -2
- package/dist/tools/metadata.tools.d.ts.map +1 -1
- package/dist/tools/metadata.tools.js +110 -75
- package/dist/tools/metadata.tools.js.map +1 -1
- package/dist/tools/org.tools.d.ts +2 -2
- package/dist/tools/org.tools.d.ts.map +1 -1
- package/dist/tools/org.tools.js +21 -15
- package/dist/tools/org.tools.js.map +1 -1
- package/dist/tools/quality.tools.d.ts +2 -2
- package/dist/tools/quality.tools.d.ts.map +1 -1
- package/dist/tools/quality.tools.js +19 -17
- package/dist/tools/quality.tools.js.map +1 -1
- package/dist/tools/query.tools.d.ts +2 -2
- package/dist/tools/query.tools.d.ts.map +1 -1
- package/dist/tools/query.tools.js +81 -58
- package/dist/tools/query.tools.js.map +1 -1
- package/dist/tools/relations.tools.d.ts +2 -2
- package/dist/tools/relations.tools.d.ts.map +1 -1
- package/dist/tools/relations.tools.js +62 -26
- package/dist/tools/relations.tools.js.map +1 -1
- package/dist/tools/search.tools.d.ts +2 -2
- package/dist/tools/search.tools.d.ts.map +1 -1
- package/dist/tools/search.tools.js +45 -45
- package/dist/tools/search.tools.js.map +1 -1
- package/dist/tools/solution.tools.d.ts +2 -2
- package/dist/tools/solution.tools.d.ts.map +1 -1
- package/dist/tools/solution.tools.js +89 -55
- package/dist/tools/solution.tools.js.map +1 -1
- package/dist/tools/teams.tools.d.ts +2 -2
- package/dist/tools/teams.tools.d.ts.map +1 -1
- package/dist/tools/teams.tools.js +33 -29
- package/dist/tools/teams.tools.js.map +1 -1
- package/dist/tools/trace.tools.d.ts +2 -2
- package/dist/tools/trace.tools.d.ts.map +1 -1
- package/dist/tools/trace.tools.js +111 -101
- package/dist/tools/trace.tools.js.map +1 -1
- package/dist/tools/tracking.tools.d.ts +2 -2
- package/dist/tools/tracking.tools.d.ts.map +1 -1
- package/dist/tools/tracking.tools.js +16 -14
- package/dist/tools/tracking.tools.js.map +1 -1
- package/dist/tools/users.tools.d.ts +2 -2
- package/dist/tools/users.tools.d.ts.map +1 -1
- package/dist/tools/users.tools.js +62 -57
- package/dist/tools/users.tools.js.map +1 -1
- package/dist/tools/validation.utils.d.ts +1 -1
- package/dist/tools/validation.utils.d.ts.map +1 -1
- package/dist/tools/validation.utils.js +9 -3
- package/dist/tools/validation.utils.js.map +1 -1
- package/dist/tools/views.tools.d.ts +2 -2
- package/dist/tools/views.tools.d.ts.map +1 -1
- package/dist/tools/views.tools.js +32 -24
- package/dist/tools/views.tools.js.map +1 -1
- package/package.json +81 -81
- package/server.json +51 -4
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
    
|
|
6
6
|
|
|
7
|
-
MCP server that exposes the Microsoft Dataverse Web API as **
|
|
7
|
+
MCP server that exposes the Microsoft Dataverse Web API as **50 AI-callable tools** — enabling GitHub Copilot, Claude, and other MCP clients to query, create, and manage Dataverse records without hallucinating schema.
|
|
8
8
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
@@ -51,13 +51,13 @@ cp config.example.json config.json
|
|
|
51
51
|
|
|
52
52
|
Edit `config.json`:
|
|
53
53
|
|
|
54
|
-
| Field
|
|
55
|
-
|
|
56
|
-
| `environmentUrl`
|
|
57
|
-
| `authMode`
|
|
58
|
-
| `pacProfileName`
|
|
59
|
-
| `requestTimeoutMs` | HTTP timeout in ms (default: `30000`)
|
|
60
|
-
| `maxRetries`
|
|
54
|
+
| Field | Description |
|
|
55
|
+
| ------------------ | ----------------------------------------------------- |
|
|
56
|
+
| `environmentUrl` | Your org URL, e.g. `https://yourorg.crm.dynamics.com` |
|
|
57
|
+
| `authMode` | `"pac"` (recommended) or `"msal"` |
|
|
58
|
+
| `pacProfileName` | PAC CLI profile name (default: `"default"`) |
|
|
59
|
+
| `requestTimeoutMs` | HTTP timeout in ms (default: `30000`) |
|
|
60
|
+
| `maxRetries` | Retry count on transient errors (default: `3`) |
|
|
61
61
|
|
|
62
62
|
### 3. Authenticate
|
|
63
63
|
|
|
@@ -82,6 +82,7 @@ npx tsx tests/live/test-whoami.ts
|
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Expected output:
|
|
85
|
+
|
|
85
86
|
```
|
|
86
87
|
WhoAmI result: { UserId: 'xxxxxxxx-...', BusinessUnitId: 'xxxxxxxx-...', OrganizationId: 'xxxxxxxx-...' }
|
|
87
88
|
```
|
|
@@ -109,58 +110,60 @@ WhoAmI result: { UserId: 'xxxxxxxx-...', BusinessUnitId: 'xxxxxxxx-...', Organiz
|
|
|
109
110
|
|
|
110
111
|
---
|
|
111
112
|
|
|
112
|
-
## Tools (
|
|
113
|
-
|
|
114
|
-
| Tool
|
|
115
|
-
|
|
116
|
-
| `dataverse_whoami`
|
|
117
|
-
| `dataverse_list_tables`
|
|
118
|
-
| `dataverse_get_table_metadata`
|
|
119
|
-
| `dataverse_get_relationships`
|
|
120
|
-
| `dataverse_list_global_option_sets`
|
|
121
|
-
| `dataverse_get_option_set`
|
|
122
|
-
| `dataverse_get_entity_key`
|
|
123
|
-
| `dataverse_query`
|
|
124
|
-
| `dataverse_execute_fetchxml`
|
|
125
|
-
| `dataverse_retrieve_multiple_with_paging`
|
|
126
|
-
| `dataverse_get`
|
|
127
|
-
| `dataverse_create`
|
|
128
|
-
| `dataverse_update`
|
|
129
|
-
| `dataverse_delete`
|
|
130
|
-
| `dataverse_upsert`
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `
|
|
149
|
-
| `
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
153
|
-
| `
|
|
154
|
-
| `
|
|
155
|
-
| `
|
|
156
|
-
| `
|
|
157
|
-
| `
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
| `
|
|
163
|
-
| `
|
|
113
|
+
## Tools (50)
|
|
114
|
+
|
|
115
|
+
| Tool | Category | Description |
|
|
116
|
+
| -------------------------------------------- | ------------- | ----------------------------------------------------------------------------------- |
|
|
117
|
+
| `dataverse_whoami` | Auth | Verify connection; returns UserId, BusinessUnitId, OrgId |
|
|
118
|
+
| `dataverse_list_tables` | Metadata | List all tables (`customOnly` filter available) |
|
|
119
|
+
| `dataverse_get_table_metadata` | Metadata | Full schema: columns, types, logical names |
|
|
120
|
+
| `dataverse_get_relationships` | Metadata | All 1:N, N:1, N:N relationships for a table; filter by `relationshipType` |
|
|
121
|
+
| `dataverse_list_global_option_sets` | Metadata | All global option sets in the environment |
|
|
122
|
+
| `dataverse_get_option_set` | Metadata | Options and values for a specific option set |
|
|
123
|
+
| `dataverse_get_entity_key` | Metadata | Alternate key definitions for a table (fields, index status, customizable flag) |
|
|
124
|
+
| `dataverse_query` | Query | OData query with `$select`, `$filter`, `$orderby`, `$expand`, `$count` |
|
|
125
|
+
| `dataverse_execute_fetchxml` | Query | Raw FetchXML for aggregations and complex joins |
|
|
126
|
+
| `dataverse_retrieve_multiple_with_paging` | Query | Paginated query following `@odata.nextLink`, with configurable `maxTotal` cap |
|
|
127
|
+
| `dataverse_get` | CRUD | Retrieve a single record by GUID |
|
|
128
|
+
| `dataverse_create` | CRUD | Create a record; returns the new GUID |
|
|
129
|
+
| `dataverse_update` | CRUD | Patch a record — only specified fields are changed |
|
|
130
|
+
| `dataverse_delete` | CRUD | Delete a record (requires explicit confirm) |
|
|
131
|
+
| `dataverse_upsert` | CRUD | Create-or-update via alternate key |
|
|
132
|
+
| `dataverse_assign` | CRUD | Assign a record to a different user or team owner |
|
|
133
|
+
| `dataverse_associate` | Relations | Associate two records via a named relationship |
|
|
134
|
+
| `dataverse_disassociate` | Relations | Remove an association between two records |
|
|
135
|
+
| `dataverse_execute_action` | Actions | Execute a global (unbound) Dataverse action |
|
|
136
|
+
| `dataverse_execute_function` | Actions | Execute a global read-only function (e.g. `WhoAmI`) |
|
|
137
|
+
| `dataverse_execute_bound_action` | Actions | Execute an action bound to a specific record |
|
|
138
|
+
| `dataverse_execute_bound_function` | Actions | Execute an OData bound function on a specific record |
|
|
139
|
+
| `dataverse_retrieve_dependencies_for_delete` | Actions | Check what components block deletion of a Dataverse component |
|
|
140
|
+
| `dataverse_list_dependencies` | Actions | List component dependencies before modifying or deleting |
|
|
141
|
+
| `dataverse_batch_execute` | Batch | Up to 1000 operations in a single HTTP batch request; optional atomic changeset |
|
|
142
|
+
| `dataverse_change_detection` | Tracking | Delta tracking using change tokens to detect record changes since last sync |
|
|
143
|
+
| `dataverse_solution_components` | Solution | List all components in a named solution; filter by component type code |
|
|
144
|
+
| `dataverse_publish_customizations` | Solution | Publish pending customizations (all or targeted entities/web resources/option sets) |
|
|
145
|
+
| `dataverse_impersonate` | Impersonation | Execute any tool on behalf of another Dataverse user via `MSCRMCallerId` |
|
|
146
|
+
| `dataverse_list_custom_actions` | Customization | Lists custom actions (custom API / SDK messages) in the environment |
|
|
147
|
+
| `dataverse_list_plugin_steps` | Customization | Lists plugin step registrations with stage, mode, entity, and state |
|
|
148
|
+
| `dataverse_get_environment_variable` | Environment | Retrieve an environment variable definition and current value |
|
|
149
|
+
| `dataverse_set_environment_variable` | Environment | Set or update an environment variable value |
|
|
150
|
+
| `dataverse_get_plugin_trace_logs` | Trace | Retrieve plugin execution trace logs for debugging |
|
|
151
|
+
| `dataverse_get_workflow_trace_logs` | Trace | Retrieve async workflow/system job execution logs |
|
|
152
|
+
| `dataverse_search` | Search | Full-text Relevance Search across all configured tables |
|
|
153
|
+
| `dataverse_get_audit_log` | Audit | Retrieve audit trail for a record showing change history |
|
|
154
|
+
| `dataverse_detect_duplicates` | Quality | Check for potential duplicates before creating a record |
|
|
155
|
+
| `dataverse_get_annotations` | Annotations | Retrieve notes and file attachments linked to a record |
|
|
156
|
+
| `dataverse_list_users` | Users | Search system users by name or email with BU filtering |
|
|
157
|
+
| `dataverse_get_user_roles` | Users | Security roles assigned to a system user |
|
|
158
|
+
| `dataverse_create_annotation` | Annotations | Create a note or file attachment linked to a record |
|
|
159
|
+
| `dataverse_get_attribute_option_set` | Metadata | Local/entity-specific option set values (statecode, statuscode, picklist) |
|
|
160
|
+
| `dataverse_list_solutions` | Solution | List all solutions in the environment |
|
|
161
|
+
| `dataverse_set_workflow_state` | Customization | Enable or disable a workflow or process |
|
|
162
|
+
| `dataverse_list_views` | Views | List system and personal saved views for a table |
|
|
163
|
+
| `dataverse_upload_file_column` | Files | Upload binary content to a file/image column on a record |
|
|
164
|
+
| `dataverse_download_file_column` | Files | Download binary content from a file/image column on a record |
|
|
165
|
+
| `dataverse_list_business_units` | Org | List business units in the environment (org hierarchy) |
|
|
166
|
+
| `dataverse_list_teams` | Teams | List Dataverse teams with optional filter by team type |
|
|
164
167
|
|
|
165
168
|
---
|
|
166
169
|
|
|
@@ -184,17 +187,17 @@ For service-principal auth (CI/CD, unattended), set `authMode: "msal"` in `confi
|
|
|
184
187
|
|
|
185
188
|
## Scripts
|
|
186
189
|
|
|
187
|
-
| Command
|
|
188
|
-
|
|
189
|
-
| `npm run build`
|
|
190
|
-
| `npm run dev`
|
|
191
|
-
| `npm start`
|
|
192
|
-
| `npm run auth:setup`
|
|
193
|
-
| `npm run typecheck`
|
|
194
|
-
| `npm run lint`
|
|
195
|
-
| `npm run test:unit`
|
|
196
|
-
| `npm run test:integration` | Integration tests
|
|
197
|
-
| `npm test`
|
|
190
|
+
| Command | Description |
|
|
191
|
+
| -------------------------- | ----------------------------------- |
|
|
192
|
+
| `npm run build` | Compile TypeScript → `dist/` |
|
|
193
|
+
| `npm run dev` | Watch mode — no build step needed |
|
|
194
|
+
| `npm start` | Start the compiled server |
|
|
195
|
+
| `npm run auth:setup` | One-time device code authentication |
|
|
196
|
+
| `npm run typecheck` | Type-check without emitting output |
|
|
197
|
+
| `npm run lint` | ESLint on `src/` |
|
|
198
|
+
| `npm run test:unit` | Unit tests only |
|
|
199
|
+
| `npm run test:integration` | Integration tests |
|
|
200
|
+
| `npm test` | All tests |
|
|
198
201
|
|
|
199
202
|
---
|
|
200
203
|
|
|
@@ -220,10 +223,9 @@ GitHub Copilot → stdio → MCP Server → Tool Router → DataverseClient →
|
|
|
220
223
|
|
|
221
224
|
## Troubleshooting
|
|
222
225
|
|
|
223
|
-
| Symptom
|
|
224
|
-
|
|
225
|
-
| `No MSAL accounts found`
|
|
226
|
-
| `"https://" is required`
|
|
227
|
-
| `pac: command not found`
|
|
228
|
-
| Server not appearing in Copilot Agent mode | Restart VS Code; check **Output → MCP** panel for errors
|
|
229
|
-
|
|
226
|
+
| Symptom | Fix |
|
|
227
|
+
| ------------------------------------------ | -------------------------------------------------------------------- |
|
|
228
|
+
| `No MSAL accounts found` | Run `npm run auth:setup` to re-authenticate |
|
|
229
|
+
| `"https://" is required` | Check `environmentUrl` in `config.json` — must start with `https://` |
|
|
230
|
+
| `pac: command not found` | Install PAC CLI and run `pac auth create --environment <url>` |
|
|
231
|
+
| Server not appearing in Copilot Agent mode | Restart VS Code; check **Output → MCP** panel for errors |
|
package/config.example.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthProvider } from
|
|
2
|
-
import type { Config } from
|
|
1
|
+
import type { AuthProvider } from "./auth-provider.interface.js";
|
|
2
|
+
import type { Config } from "../config/config.schema.js";
|
|
3
3
|
export declare function createAuthProvider(config: Config): AuthProvider;
|
|
4
4
|
//# sourceMappingURL=auth-provider.factory.d.ts.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { PacAuthProvider } from
|
|
2
|
-
import { MsalAuthProvider } from
|
|
1
|
+
import { PacAuthProvider } from "./pac-auth-provider.js";
|
|
2
|
+
import { MsalAuthProvider } from "./msal-auth-provider.js";
|
|
3
3
|
export function createAuthProvider(config) {
|
|
4
4
|
switch (config.authMode) {
|
|
5
|
-
case
|
|
5
|
+
case "pac":
|
|
6
6
|
return new PacAuthProvider(config.environmentUrl);
|
|
7
|
-
case
|
|
7
|
+
case "msal":
|
|
8
8
|
return new MsalAuthProvider(config);
|
|
9
9
|
default: {
|
|
10
10
|
const _exhaustive = config.authMode;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AuthProvider } from
|
|
2
|
-
import type { Config } from
|
|
1
|
+
import type { AuthProvider } from "./auth-provider.interface.js";
|
|
2
|
+
import type { Config } from "../config/config.schema.js";
|
|
3
3
|
export declare class MsalAuthProvider implements AuthProvider {
|
|
4
4
|
readonly environmentUrl: string;
|
|
5
5
|
private readonly msalApp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"msal-auth-provider.d.ts","sourceRoot":"","sources":["../../src/auth/msal-auth-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAqB;gBAE/B,MAAM,EAAE,MAAM;
|
|
1
|
+
{"version":3,"file":"msal-auth-provider.d.ts","sourceRoot":"","sources":["../../src/auth/msal-auth-provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAEzD,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAqB;gBAE/B,MAAM,EAAE,MAAM;IAgCpB,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IA6BjC,eAAe,IAAI,IAAI;IAKjB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;CAQ1C"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConfidentialClientApplication } from
|
|
1
|
+
import { ConfidentialClientApplication } from "@azure/msal-node";
|
|
2
2
|
export class MsalAuthProvider {
|
|
3
3
|
environmentUrl;
|
|
4
4
|
msalApp;
|
|
@@ -8,13 +8,13 @@ export class MsalAuthProvider {
|
|
|
8
8
|
constructor(config) {
|
|
9
9
|
this.environmentUrl = config.environmentUrl;
|
|
10
10
|
if (!config.tenantId) {
|
|
11
|
-
throw new Error(
|
|
11
|
+
throw new Error("MsalAuthProvider requires config.tenantId for client_credentials flow");
|
|
12
12
|
}
|
|
13
13
|
if (!config.clientId) {
|
|
14
|
-
throw new Error(
|
|
14
|
+
throw new Error("MsalAuthProvider requires config.clientId for client_credentials flow");
|
|
15
15
|
}
|
|
16
16
|
if (!config.clientSecret) {
|
|
17
|
-
throw new Error(
|
|
17
|
+
throw new Error("MsalAuthProvider requires config.clientSecret for client_credentials flow");
|
|
18
18
|
}
|
|
19
19
|
this.msalApp = new ConfidentialClientApplication({
|
|
20
20
|
auth: {
|
|
@@ -24,19 +24,21 @@ export class MsalAuthProvider {
|
|
|
24
24
|
},
|
|
25
25
|
});
|
|
26
26
|
// Scope for Dataverse client_credentials: <environmentUrl>/.default
|
|
27
|
-
const baseUrl = config.environmentUrl.replace(/\/$/,
|
|
27
|
+
const baseUrl = config.environmentUrl.replace(/\/$/, "");
|
|
28
28
|
this.scope = `${baseUrl}/.default`;
|
|
29
29
|
}
|
|
30
30
|
async getToken() {
|
|
31
31
|
// Return cached token if still valid (60-second buffer before expiry)
|
|
32
|
-
if (this.cachedToken !== null &&
|
|
32
|
+
if (this.cachedToken !== null &&
|
|
33
|
+
this.tokenExpiresAt !== null &&
|
|
34
|
+
new Date() < this.tokenExpiresAt) {
|
|
33
35
|
return this.cachedToken;
|
|
34
36
|
}
|
|
35
37
|
const result = await this.msalApp.acquireTokenByClientCredential({
|
|
36
38
|
scopes: [this.scope],
|
|
37
39
|
});
|
|
38
40
|
if (result === null || !result.accessToken) {
|
|
39
|
-
throw new Error(
|
|
41
|
+
throw new Error("MSAL: acquireTokenByClientCredential returned no access token");
|
|
40
42
|
}
|
|
41
43
|
this.cachedToken = result.accessToken;
|
|
42
44
|
// Subtract 60 seconds from expiry to avoid using a token that is about to expire
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"msal-auth-provider.js","sourceRoot":"","sources":["../../src/auth/msal-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAIjE,MAAM,OAAO,gBAAgB;IAClB,cAAc,CAAS;IACf,OAAO,CAAgC;IACvC,KAAK,CAAS;IACvB,WAAW,GAAkB,IAAI,CAAC;IAClC,cAAc,GAAgB,IAAI,CAAC;IAE3C,YAAY,MAAc;QACxB,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;QAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"msal-auth-provider.js","sourceRoot":"","sources":["../../src/auth/msal-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAIjE,MAAM,OAAO,gBAAgB;IAClB,cAAc,CAAS;IACf,OAAO,CAAgC;IACvC,KAAK,CAAS;IACvB,WAAW,GAAkB,IAAI,CAAC;IAClC,cAAc,GAAgB,IAAI,CAAC;IAE3C,YAAY,MAAc;QACxB,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;QAE5C,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,uEAAuE,CACxE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,uEAAuE,CACxE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,6BAA6B,CAAC;YAC/C,IAAI,EAAE;gBACJ,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,SAAS,EAAE,qCAAqC,MAAM,CAAC,QAAQ,EAAE;aAClE;SACF,CAAC,CAAC;QAEH,oEAAoE;QACpE,MAAM,OAAO,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACzD,IAAI,CAAC,KAAK,GAAG,GAAG,OAAO,WAAW,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,sEAAsE;QACtE,IACE,IAAI,CAAC,WAAW,KAAK,IAAI;YACzB,IAAI,CAAC,cAAc,KAAK,IAAI;YAC5B,IAAI,IAAI,EAAE,GAAG,IAAI,CAAC,cAAc,EAChC,CAAC;YACD,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,8BAA8B,CAAC;YAC/D,MAAM,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;SACrB,CAAC,CAAC;QAEH,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACb,+DAA+D,CAChE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QACtC,iFAAiF;QACjF,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,SAAS;YACpC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC;YAC/C,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC,uBAAuB;QAE7D,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,eAAe;QACb,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pac-auth-provider.d.ts","sourceRoot":"","sources":["../../src/auth/pac-auth-provider.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pac-auth-provider.d.ts","sourceRoot":"","sources":["../../src/auth/pac-auth-provider.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA+EjE,qBAAa,eAAgB,YAAW,YAAY;IAClD,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA0B;IAC9C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAa;gBAEvB,cAAc,EAAE,MAAM;IAY5B,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAQjC,eAAe,IAAI,IAAI;IAKjB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IASzC;;;OAGG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;YAY3B,YAAY;IAiC1B,OAAO,CAAC,WAAW;CAKpB"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { PublicClientApplication, } from
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from
|
|
3
|
-
import { join } from
|
|
4
|
-
import { createCipheriv, createDecipheriv, createHash, randomBytes } from
|
|
1
|
+
import { PublicClientApplication, } from "@azure/msal-node";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes, } from "crypto";
|
|
5
5
|
// Microsoft Power Platform CLI App ID — public client, no app registration needed
|
|
6
|
-
const PLATFORM_CLIENT_ID =
|
|
7
|
-
const TOKEN_CACHE_FILE = join(process.cwd(),
|
|
6
|
+
const PLATFORM_CLIENT_ID = "1950a258-227b-4e31-a9cf-717495945fc2";
|
|
7
|
+
const TOKEN_CACHE_FILE = join(process.cwd(), ".msal-cache.json");
|
|
8
8
|
/**
|
|
9
9
|
* Derives a machine+user-scoped encryption key.
|
|
10
10
|
* Not a substitute for OS-level credential storage (DPAPI/keytar), but prevents
|
|
@@ -12,41 +12,44 @@ const TOKEN_CACHE_FILE = join(process.cwd(), '.msal-cache.json');
|
|
|
12
12
|
*/
|
|
13
13
|
function getDerivedKey() {
|
|
14
14
|
const seed = [
|
|
15
|
-
process.env[
|
|
16
|
-
process.env[
|
|
17
|
-
|
|
18
|
-
].join(
|
|
19
|
-
return createHash(
|
|
15
|
+
process.env["COMPUTERNAME"] ?? process.env["HOSTNAME"] ?? "",
|
|
16
|
+
process.env["USERNAME"] ?? process.env["USER"] ?? "",
|
|
17
|
+
"mcp-dataverse-cache-v1",
|
|
18
|
+
].join(".");
|
|
19
|
+
return createHash("sha256").update(seed).digest();
|
|
20
20
|
}
|
|
21
21
|
function encryptForDisk(plaintext) {
|
|
22
22
|
const key = getDerivedKey();
|
|
23
23
|
const iv = randomBytes(16);
|
|
24
|
-
const cipher = createCipheriv(
|
|
25
|
-
const encrypted = Buffer.concat([
|
|
24
|
+
const cipher = createCipheriv("aes-256-gcm", key, iv);
|
|
25
|
+
const encrypted = Buffer.concat([
|
|
26
|
+
cipher.update(plaintext, "utf-8"),
|
|
27
|
+
cipher.final(),
|
|
28
|
+
]);
|
|
26
29
|
return JSON.stringify({
|
|
27
30
|
v: 1,
|
|
28
|
-
iv: iv.toString(
|
|
29
|
-
tag: cipher.getAuthTag().toString(
|
|
30
|
-
d: encrypted.toString(
|
|
31
|
+
iv: iv.toString("hex"),
|
|
32
|
+
tag: cipher.getAuthTag().toString("hex"),
|
|
33
|
+
d: encrypted.toString("hex"),
|
|
31
34
|
});
|
|
32
35
|
}
|
|
33
36
|
function decryptFromDisk(raw) {
|
|
34
37
|
const parsed = JSON.parse(raw);
|
|
35
|
-
if (parsed[
|
|
36
|
-
throw new Error(
|
|
37
|
-
const iv = Buffer.from(parsed[
|
|
38
|
-
const tag = Buffer.from(parsed[
|
|
39
|
-
const encrypted = Buffer.from(parsed[
|
|
40
|
-
const decipher = createDecipheriv(
|
|
38
|
+
if (parsed["v"] !== 1)
|
|
39
|
+
throw new Error("Unknown cache format version");
|
|
40
|
+
const iv = Buffer.from(parsed["iv"], "hex");
|
|
41
|
+
const tag = Buffer.from(parsed["tag"], "hex");
|
|
42
|
+
const encrypted = Buffer.from(parsed["d"], "hex");
|
|
43
|
+
const decipher = createDecipheriv("aes-256-gcm", getDerivedKey(), iv);
|
|
41
44
|
decipher.setAuthTag(tag);
|
|
42
|
-
return decipher.update(encrypted).toString(
|
|
45
|
+
return decipher.update(encrypted).toString("utf-8") + decipher.final("utf-8");
|
|
43
46
|
}
|
|
44
47
|
function createCachePlugin() {
|
|
45
48
|
return {
|
|
46
49
|
beforeCacheAccess: async (cacheContext) => {
|
|
47
50
|
if (existsSync(TOKEN_CACHE_FILE)) {
|
|
48
51
|
try {
|
|
49
|
-
const raw = readFileSync(TOKEN_CACHE_FILE,
|
|
52
|
+
const raw = readFileSync(TOKEN_CACHE_FILE, "utf-8");
|
|
50
53
|
// Support both encrypted (v1) and legacy plaintext caches
|
|
51
54
|
let serialized;
|
|
52
55
|
try {
|
|
@@ -65,7 +68,7 @@ function createCachePlugin() {
|
|
|
65
68
|
},
|
|
66
69
|
afterCacheAccess: async (cacheContext) => {
|
|
67
70
|
if (cacheContext.cacheHasChanged) {
|
|
68
|
-
writeFileSync(TOKEN_CACHE_FILE, encryptForDisk(cacheContext.tokenCache.serialize()), { encoding:
|
|
71
|
+
writeFileSync(TOKEN_CACHE_FILE, encryptForDisk(cacheContext.tokenCache.serialize()), { encoding: "utf-8", mode: 0o600 });
|
|
69
72
|
}
|
|
70
73
|
},
|
|
71
74
|
};
|
|
@@ -76,11 +79,11 @@ export class PacAuthProvider {
|
|
|
76
79
|
cachedToken = null;
|
|
77
80
|
tokenExpiresAt = 0;
|
|
78
81
|
constructor(environmentUrl) {
|
|
79
|
-
this.environmentUrl = environmentUrl.replace(/\/$/,
|
|
82
|
+
this.environmentUrl = environmentUrl.replace(/\/$/, "");
|
|
80
83
|
this.pca = new PublicClientApplication({
|
|
81
84
|
auth: {
|
|
82
85
|
clientId: PLATFORM_CLIENT_ID,
|
|
83
|
-
authority:
|
|
86
|
+
authority: "https://login.microsoftonline.com/common",
|
|
84
87
|
},
|
|
85
88
|
cache: { cachePlugin: createCachePlugin() },
|
|
86
89
|
});
|
|
@@ -123,9 +126,9 @@ export class PacAuthProvider {
|
|
|
123
126
|
async refreshToken() {
|
|
124
127
|
const accounts = await this.pca.getAllAccounts();
|
|
125
128
|
if (accounts.length === 0) {
|
|
126
|
-
throw new Error(
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
throw new Error("No authenticated account found.\n" +
|
|
130
|
+
"Run once: npm run auth:setup\n" +
|
|
131
|
+
"Then restart the server.");
|
|
129
132
|
}
|
|
130
133
|
try {
|
|
131
134
|
const result = await this.pca.acquireTokenSilent({
|
|
@@ -133,21 +136,22 @@ export class PacAuthProvider {
|
|
|
133
136
|
account: accounts[0],
|
|
134
137
|
});
|
|
135
138
|
if (!result?.accessToken) {
|
|
136
|
-
throw new Error(
|
|
139
|
+
throw new Error("Silent token acquisition returned empty token");
|
|
137
140
|
}
|
|
138
141
|
this.cacheResult(result);
|
|
139
142
|
return result.accessToken;
|
|
140
143
|
}
|
|
141
144
|
catch {
|
|
142
145
|
this.cachedToken = null;
|
|
143
|
-
throw new Error(
|
|
144
|
-
|
|
145
|
-
|
|
146
|
+
throw new Error("Token refresh failed. Re-authenticate:\n" +
|
|
147
|
+
"npm run auth:setup\n" +
|
|
148
|
+
"Then restart the server.");
|
|
146
149
|
}
|
|
147
150
|
}
|
|
148
151
|
cacheResult(result) {
|
|
149
152
|
this.cachedToken = result.accessToken;
|
|
150
|
-
this.tokenExpiresAt =
|
|
153
|
+
this.tokenExpiresAt =
|
|
154
|
+
result.expiresOn?.getTime() ?? Date.now() + 55 * 60 * 1000;
|
|
151
155
|
}
|
|
152
156
|
}
|
|
153
157
|
//# sourceMappingURL=pac-auth-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pac-auth-provider.js","sourceRoot":"","sources":["../../src/auth/pac-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,GAIxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,
|
|
1
|
+
{"version":3,"file":"pac-auth-provider.js","sourceRoot":"","sources":["../../src/auth/pac-auth-provider.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,GAIxB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EACL,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,WAAW,GACZ,MAAM,QAAQ,CAAC;AAGhB,kFAAkF;AAClF,MAAM,kBAAkB,GAAG,sCAAsC,CAAC;AAClE,MAAM,gBAAgB,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,kBAAkB,CAAC,CAAC;AAEjE;;;;GAIG;AACH,SAAS,aAAa;IACpB,MAAM,IAAI,GAAG;QACX,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,EAAE;QAC5D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE;QACpD,wBAAwB;KACzB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACZ,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;AACpD,CAAC;AAED,SAAS,cAAc,CAAC,SAAiB;IACvC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,MAAM,EAAE,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAG,cAAc,CAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC;QAC9B,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC;QACjC,MAAM,CAAC,KAAK,EAAE;KACf,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,CAAC,EAAE,CAAC;QACJ,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;QACtB,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC;QACxC,CAAC,EAAE,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC;KAC7B,CAAC,CAAC;AACL,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IAClC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA4B,CAAC;IAC1D,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACvE,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAW,EAAE,KAAK,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAW,EAAE,KAAK,CAAC,CAAC;IACxD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAW,EAAE,KAAK,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,aAAa,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC;IACtE,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IACzB,OAAO,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAChF,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO;QACL,iBAAiB,EAAE,KAAK,EAAE,YAA+B,EAAE,EAAE;YAC3D,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC;oBACpD,0DAA0D;oBAC1D,IAAI,UAAkB,CAAC;oBACvB,IAAI,CAAC;wBACH,UAAU,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;oBACpC,CAAC;oBAAC,MAAM,CAAC;wBACP,wEAAwE;wBACxE,UAAU,GAAG,GAAG,CAAC;oBACnB,CAAC;oBACD,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;gBAClD,CAAC;gBAAC,MAAM,CAAC;oBACP,4DAA4D;gBAC9D,CAAC;YACH,CAAC;QACH,CAAC;QACD,gBAAgB,EAAE,KAAK,EAAE,YAA+B,EAAE,EAAE;YAC1D,IAAI,YAAY,CAAC,eAAe,EAAE,CAAC;gBACjC,aAAa,CACX,gBAAgB,EAChB,cAAc,CAAC,YAAY,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,EACnD,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CACnC,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,eAAe;IACjB,cAAc,CAAS;IACf,GAAG,CAA0B;IACtC,WAAW,GAAkB,IAAI,CAAC;IAClC,cAAc,GAAW,CAAC,CAAC;IAEnC,YAAY,cAAsB;QAChC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAExD,IAAI,CAAC,GAAG,GAAG,IAAI,uBAAuB,CAAC;YACrC,IAAI,EAAE;gBACJ,QAAQ,EAAE,kBAAkB;gBAC5B,SAAS,EAAE,0CAA0C;aACtD;YACD,KAAK,EAAE,EAAE,WAAW,EAAE,iBAAiB,EAAE,EAAE;SAC5C,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,IAAI,CAAC,cAAc,GAAG,GAAG,GAAG,MAAM,EAAE,CAAC;YACpE,OAAO,IAAI,CAAC,WAAW,CAAC;QAC1B,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;IAC7B,CAAC;IAED,eAAe;QACb,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;YACtB,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,wBAAwB,CAAC;YACrD,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,WAAW,CAAC;YAC3C,kBAAkB,EAAE,CAAC,QAAQ,EAAE,EAAE;gBAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;YAClD,CAAC;SACF,CAAC,CAAC;QACH,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;QAEjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,mCAAmC;gBACjC,gCAAgC;gBAChC,0BAA0B,CAC7B,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;gBAC/C,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,cAAc,WAAW,CAAC;gBAC3C,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAE;aACtB,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;YACnE,CAAC;YAED,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACzB,OAAO,MAAM,CAAC,WAAW,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,0CAA0C;gBACxC,sBAAsB;gBACtB,0BAA0B,CAC7B,CAAC;QACJ,CAAC;IACH,CAAC;IAEO,WAAW,CAAC,MAA4B;QAC9C,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;QACtC,IAAI,CAAC,cAAc;YACjB,MAAM,CAAC,SAAS,EAAE,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAC/D,CAAC;CACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.loader.d.ts","sourceRoot":"","sources":["../../src/config/config.loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAI/D,wBAAgB,UAAU,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"config.loader.d.ts","sourceRoot":"","sources":["../../src/config/config.loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAI/D,wBAAgB,UAAU,IAAI,MAAM,CAoDnC"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { ConfigSchema } from
|
|
4
|
-
const CONFIG_FILE_NAME =
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { ConfigSchema } from "./config.schema.js";
|
|
4
|
+
const CONFIG_FILE_NAME = "config.json";
|
|
5
5
|
export function loadConfig() {
|
|
6
6
|
// Priority: env vars > config.json > defaults
|
|
7
7
|
const configPath = join(process.cwd(), CONFIG_FILE_NAME);
|
|
8
8
|
let rawConfig = {};
|
|
9
9
|
if (existsSync(configPath)) {
|
|
10
|
-
const fileContent = readFileSync(configPath,
|
|
10
|
+
const fileContent = readFileSync(configPath, "utf-8");
|
|
11
11
|
try {
|
|
12
12
|
rawConfig = JSON.parse(fileContent);
|
|
13
13
|
}
|
|
@@ -16,36 +16,33 @@ export function loadConfig() {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
// Override with env vars if present
|
|
19
|
-
if (process.env[
|
|
20
|
-
rawConfig[
|
|
19
|
+
if (process.env["DATAVERSE_ENV_URL"]) {
|
|
20
|
+
rawConfig["environmentUrl"] = process.env["DATAVERSE_ENV_URL"];
|
|
21
21
|
}
|
|
22
|
-
if (process.env[
|
|
23
|
-
rawConfig[
|
|
22
|
+
if (process.env["AUTH_MODE"]) {
|
|
23
|
+
rawConfig["authMode"] = process.env["AUTH_MODE"];
|
|
24
24
|
}
|
|
25
|
-
if (process.env[
|
|
26
|
-
rawConfig[
|
|
25
|
+
if (process.env["PAC_PROFILE_NAME"]) {
|
|
26
|
+
rawConfig["pacProfileName"] = process.env["PAC_PROFILE_NAME"];
|
|
27
27
|
}
|
|
28
|
-
if (process.env[
|
|
29
|
-
rawConfig[
|
|
28
|
+
if (process.env["TENANT_ID"]) {
|
|
29
|
+
rawConfig["tenantId"] = process.env["TENANT_ID"];
|
|
30
30
|
}
|
|
31
|
-
if (process.env[
|
|
32
|
-
rawConfig[
|
|
31
|
+
if (process.env["CLIENT_ID"]) {
|
|
32
|
+
rawConfig["clientId"] = process.env["CLIENT_ID"];
|
|
33
33
|
}
|
|
34
|
-
if (process.env[
|
|
35
|
-
rawConfig[
|
|
34
|
+
if (process.env["CLIENT_SECRET"]) {
|
|
35
|
+
rawConfig["clientSecret"] = process.env["CLIENT_SECRET"];
|
|
36
36
|
}
|
|
37
|
-
if (process.env[
|
|
38
|
-
rawConfig[
|
|
37
|
+
if (process.env["REQUEST_TIMEOUT_MS"]) {
|
|
38
|
+
rawConfig["requestTimeoutMs"] = Number(process.env["REQUEST_TIMEOUT_MS"]);
|
|
39
39
|
}
|
|
40
|
-
if (process.env[
|
|
41
|
-
rawConfig[
|
|
42
|
-
}
|
|
43
|
-
if (process.env['MAX_RETRIES']) {
|
|
44
|
-
rawConfig['maxRetries'] = Number(process.env['MAX_RETRIES']);
|
|
40
|
+
if (process.env["MAX_RETRIES"]) {
|
|
41
|
+
rawConfig["maxRetries"] = Number(process.env["MAX_RETRIES"]);
|
|
45
42
|
}
|
|
46
43
|
const result = ConfigSchema.safeParse(rawConfig);
|
|
47
44
|
if (!result.success) {
|
|
48
|
-
throw new Error(`Invalid configuration:\n${result.error.issues.map(i => ` - ${i.path.join(
|
|
45
|
+
throw new Error(`Invalid configuration:\n${result.error.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n")}`);
|
|
49
46
|
}
|
|
50
47
|
return result.data;
|
|
51
48
|
}
|