@pptb/types 1.2.4-beta.0 → 1.2.4-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -150
- package/bin/pptb-validate.js +1 -243
- package/index.d.ts +1 -4
- package/package.json +4 -3
- package/lib/validate.js +0 -486
- package/powerplatformAPI.d.ts +0 -200
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ TypeScript type definitions for Power Platform ToolBox APIs, plus a built-in CLI
|
|
|
8
8
|
- [Quick start](#quick-start)
|
|
9
9
|
- [CLI options](#cli-options)
|
|
10
10
|
- [What is validated](#what-is-validated)
|
|
11
|
-
|
|
11
|
+
- [pptb.config.json (optional)](#pptbconfigjson-optional)
|
|
12
12
|
- [Overview](#overview)
|
|
13
13
|
- [Usage](#usage)
|
|
14
14
|
- [Include all type definitions](#include-all-type-definitions)
|
|
@@ -19,22 +19,12 @@ TypeScript type definitions for Power Platform ToolBox APIs, plus a built-in CLI
|
|
|
19
19
|
- [Terminal Operations](#terminal-operations)
|
|
20
20
|
- [Events](#events)
|
|
21
21
|
- [Inter-Tool Invocation](#inter-tool-invocation)
|
|
22
|
-
- [Caller: launching another tool with prefill data](#caller-launching-another-tool-with-prefill-data)
|
|
23
|
-
- [Caller: tag-based capability discovery](#caller-tag-based-capability-discovery)
|
|
24
|
-
- [Callee: reading prefill data and returning a result](#callee-reading-prefill-data-and-returning-a-result)
|
|
25
|
-
- [Declaring your invocation contract](#declaring-your-invocation-contract)
|
|
26
22
|
- [Dataverse API Examples](#dataverse-api-examples)
|
|
27
23
|
- [CRUD Operations](#crud-operations)
|
|
28
24
|
- [FetchXML Queries](#fetchxml-queries)
|
|
29
25
|
- [Metadata Operations](#metadata-operations)
|
|
30
26
|
- [Execute Actions/Functions](#execute-actionsfunctions)
|
|
31
27
|
- [Deploy Solutions](#deploy-solutions)
|
|
32
|
-
- [Power Platform API Examples](#power-platform-api-examples)
|
|
33
|
-
- [Using Power Apps API](#using-power-apps-api)
|
|
34
|
-
- [Using Power Automate API](#using-power-automate-api)
|
|
35
|
-
- [Using Environment Management API](#using-environment-management-api)
|
|
36
|
-
- [Using Governance API](#using-governance-api)
|
|
37
|
-
- [Available Categories](#available-categories)
|
|
38
28
|
- [API Reference](#api-reference)
|
|
39
29
|
- [ToolBox API (`window.toolboxAPI`)](#toolbox-api-windowtoolboxapi)
|
|
40
30
|
- [Connections](#connections-1)
|
|
@@ -47,8 +37,6 @@ TypeScript type definitions for Power Platform ToolBox APIs, plus a built-in CLI
|
|
|
47
37
|
- [Query Operations](#query-operations)
|
|
48
38
|
- [Metadata Operations](#metadata-operations-1)
|
|
49
39
|
- [Advanced Operations](#advanced-operations)
|
|
50
|
-
- [Power Platform API (`window.powerplatformAPI`)](#power-platform-api-windowpowerplatformapi)
|
|
51
|
-
- [Category Methods](#category-methods)
|
|
52
40
|
- [Security Notes](#security-notes)
|
|
53
41
|
- [Publishing the package to npm](#publishing-the-package-to-npm)
|
|
54
42
|
- [License](#license)
|
|
@@ -128,14 +116,14 @@ The validator checks every field that the official review pipeline inspects:
|
|
|
128
116
|
|
|
129
117
|
In addition to `package.json`, the validator automatically checks a `pptb.config.json` file if one is present in the same directory. This file declares tool-to-tool communication contracts and other PPTB-specific metadata.
|
|
130
118
|
|
|
131
|
-
| Field
|
|
132
|
-
|
|
|
133
|
-
| `invocation.version`
|
|
134
|
-
| `invocation.capabilities`
|
|
135
|
-
| `invocation.prefill`
|
|
136
|
-
| `invocation.prefill.properties`
|
|
137
|
-
| `invocation.returnTopic`
|
|
138
|
-
| `invocation.returnTopic.properties`
|
|
119
|
+
| Field | Required | Rules |
|
|
120
|
+
| --------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `invocation.version` | ✅\*\* | Must be a valid **semantic version** string (e.g. `"1.0.0"`). Tool developers own this version and bump it when the invocation contract changes. |
|
|
122
|
+
| `invocation.capabilities` | ❌ | Array of non-empty string tags (e.g. `["entity-picker"]`). Used by callers to discover this tool via `findToolsByCapability`. |
|
|
123
|
+
| `invocation.prefill` | ❌ | JSON-schema-style object describing data callers can pre-populate |
|
|
124
|
+
| `invocation.prefill.properties` | ❌ | Map of property names to `{ type?, enum?, items? }` descriptors |
|
|
125
|
+
| `invocation.returnTopic` | ❌ | JSON-schema-style object describing the data this tool returns to its caller |
|
|
126
|
+
| `invocation.returnTopic.properties` | ❌ | Map of property names to `{ type?, enum?, items? }` descriptors |
|
|
139
127
|
|
|
140
128
|
> \*\* Required only when the `invocation` object is present.
|
|
141
129
|
|
|
@@ -165,11 +153,10 @@ In addition to `package.json`, the validator automatically checks a `pptb.config
|
|
|
165
153
|
|
|
166
154
|
## Overview
|
|
167
155
|
|
|
168
|
-
The `@pptb/types` package provides TypeScript definitions for
|
|
156
|
+
The `@pptb/types` package provides TypeScript definitions for two main APIs:
|
|
169
157
|
|
|
170
158
|
1. **ToolBox API** (`window.toolboxAPI`) - Core platform features (connections, utilities, terminals, events)
|
|
171
159
|
2. **Dataverse API** (`window.dataverseAPI`) - Microsoft Dataverse Web API operations
|
|
172
|
-
3. **Power Platform API** (`window.powerplatformAPI`) - Power Platform Admin APIs with generic HTTP methods
|
|
173
160
|
|
|
174
161
|
## Usage
|
|
175
162
|
|
|
@@ -178,10 +165,9 @@ The `@pptb/types` package provides TypeScript definitions for three main APIs:
|
|
|
178
165
|
```typescript
|
|
179
166
|
/// <reference types="@pptb/types" />
|
|
180
167
|
|
|
181
|
-
//
|
|
168
|
+
// Both APIs are now available
|
|
182
169
|
const toolbox = window.toolboxAPI;
|
|
183
170
|
const dataverse = window.dataverseAPI;
|
|
184
|
-
const powerplatform = window.powerplatformAPI;
|
|
185
171
|
```
|
|
186
172
|
|
|
187
173
|
### Include specific API types
|
|
@@ -192,9 +178,6 @@ const powerplatform = window.powerplatformAPI;
|
|
|
192
178
|
|
|
193
179
|
// Only Dataverse API types
|
|
194
180
|
/// <reference types="@pptb/types/dataverseAPI" />
|
|
195
|
-
|
|
196
|
-
// Only Power Platform API types
|
|
197
|
-
/// <reference types="@pptb/types/powerplatformAPI" />
|
|
198
181
|
```
|
|
199
182
|
|
|
200
183
|
## ToolBox API Examples
|
|
@@ -316,7 +299,10 @@ Tools can launch one another and pass data between them using the `invocation` n
|
|
|
316
299
|
```typescript
|
|
317
300
|
// Tool A – launches the entity-picker tool and waits for a selection
|
|
318
301
|
// The callee automatically inherits this tool's FXS connection
|
|
319
|
-
const result = await toolboxAPI.invocation.launchTool(
|
|
302
|
+
const result = await toolboxAPI.invocation.launchTool(
|
|
303
|
+
"@my-org/entity-picker",
|
|
304
|
+
{ entityName: "account", allowMultiSelect: false },
|
|
305
|
+
);
|
|
320
306
|
|
|
321
307
|
if (result !== null) {
|
|
322
308
|
console.log("Selected record id:", (result as { selectedId: string }).selectedId);
|
|
@@ -353,7 +339,7 @@ if (ctx) {
|
|
|
353
339
|
}
|
|
354
340
|
```
|
|
355
341
|
|
|
356
|
-
> **Tip:** A tool that was
|
|
342
|
+
> **Tip:** A tool that was *not* launched by another tool receives `null` from `getLaunchContext()`.
|
|
357
343
|
> Use this to show a standalone UI or redirect accordingly.
|
|
358
344
|
|
|
359
345
|
> **Auto-close**: after calling `returnData`, PPTB automatically closes the callee window. The callee does **not** need to close itself.
|
|
@@ -537,84 +523,9 @@ await waitForImport(result.ImportJobId);
|
|
|
537
523
|
|
|
538
524
|
> **Note:** `deploySolution` automatically supplies `PublishWorkflows` and `OverwriteUnmanagedCustomizations` with a default value of `false` when you do not specify them, aligning with Dataverse's ImportSolution requirements.
|
|
539
525
|
|
|
540
|
-
## Power Platform API Examples
|
|
541
|
-
|
|
542
|
-
The Power Platform API provides generic HTTP methods for all Power Platform Admin API categories:
|
|
543
|
-
|
|
544
|
-
### Using Power Apps API
|
|
545
|
-
|
|
546
|
-
```typescript
|
|
547
|
-
// Get an admin app
|
|
548
|
-
const app = await powerplatformAPI.PowerApps.Get("environments/{environmentId}/apps/{app}?api-version=2024-10-01");
|
|
549
|
-
|
|
550
|
-
// List all apps in an environment
|
|
551
|
-
const apps = await powerplatformAPI.PowerApps.Get("environments/{environmentId}/apps?api-version=2024-10-01");
|
|
552
|
-
|
|
553
|
-
// Create a new app
|
|
554
|
-
await powerplatformAPI.PowerApps.Post("environments/{environmentId}/apps?api-version=2024-10-01", {
|
|
555
|
-
name: "My App",
|
|
556
|
-
environmentId: "{environmentId}",
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
// Update an app
|
|
560
|
-
await powerplatformAPI.PowerApps.Patch("environments/{environmentId}/apps/{appId}?api-version=2024-10-01", {
|
|
561
|
-
name: "Updated App Name",
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
// Delete an app
|
|
565
|
-
await powerplatformAPI.PowerApps.Delete("environments/{environmentId}/apps/{appId}?api-version=2024-10-01");
|
|
566
|
-
```
|
|
567
|
-
|
|
568
|
-
### Using Power Automate API
|
|
569
|
-
|
|
570
|
-
```typescript
|
|
571
|
-
// List flows
|
|
572
|
-
const flows = await powerplatformAPI.PowerAutomate.Get("environments/{environmentId}/flows?api-version=2024-10-01");
|
|
573
|
-
|
|
574
|
-
// Get flow details
|
|
575
|
-
const flow = await powerplatformAPI.PowerAutomate.Get("environments/{environmentId}/flows/{flowId}?api-version=2024-10-01");
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
### Using Environment Management API
|
|
579
|
-
|
|
580
|
-
```typescript
|
|
581
|
-
// List environments
|
|
582
|
-
const environments = await powerplatformAPI.EnvironmentManagement.Get("environments?api-version=2024-10-01");
|
|
583
|
-
|
|
584
|
-
// Get environment details
|
|
585
|
-
const env = await powerplatformAPI.EnvironmentManagement.Get("environments/{environmentId}?api-version=2024-10-01");
|
|
586
|
-
```
|
|
587
|
-
|
|
588
|
-
### Using Governance API
|
|
589
|
-
|
|
590
|
-
```typescript
|
|
591
|
-
// Get governance data
|
|
592
|
-
const data = await powerplatformAPI.Governance.Get("environments/{environmentId}/governance?api-version=2024-10-01");
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
### Available Categories
|
|
596
|
-
|
|
597
|
-
The Power Platform API exposes the following categories, each with `Get`, `Post`, `Put`, `Patch`, and `Delete` methods:
|
|
598
|
-
|
|
599
|
-
- **Analytics** - `https://api.powerplatform.com/analytics`
|
|
600
|
-
- **AppManagement** - `https://api.powerplatform.com/appmanagement`
|
|
601
|
-
- **Authorization** - `https://api.powerplatform.com/authorization`
|
|
602
|
-
- **Connectivity** - `https://api.powerplatform.com/connectivity`
|
|
603
|
-
- **CopilotStudio** - `https://api.powerplatform.com/copilotstudio`
|
|
604
|
-
- **Dynamics** - `https://api.powerplatform.com/dynamics`
|
|
605
|
-
- **EnvironmentManagement** - `https://api.powerplatform.com/environmentmanagement`
|
|
606
|
-
- **Governance** - `https://api.powerplatform.com/governance`
|
|
607
|
-
- **Licensing** - `https://api.powerplatform.com/licensing`
|
|
608
|
-
- **PowerApps** - `https://api.powerplatform.com/powerapps`
|
|
609
|
-
- **PowerAutomate** - `https://api.powerplatform.com/powerautomate`
|
|
610
|
-
- **PowerPages** - `https://api.powerplatform.com/powerpages`
|
|
611
|
-
- **ResourceQuery** - `https://api.powerplatform.com/resourcequery`
|
|
612
|
-
- **UserManagement** - `https://api.powerplatform.com/usermanagement`
|
|
613
|
-
- **WorkflowAgents** - `https://api.powerplatform.com/workflowagents`
|
|
614
|
-
|
|
615
526
|
## API Reference
|
|
616
527
|
|
|
617
|
-
The Power Platform ToolBox exposes
|
|
528
|
+
The Power Platform ToolBox exposes two main APIs to tools:
|
|
618
529
|
|
|
619
530
|
### ToolBox API (`window.toolboxAPI`)
|
|
620
531
|
|
|
@@ -786,50 +697,6 @@ Complete HTTP client for interacting with Microsoft Dataverse:
|
|
|
786
697
|
- Returns import job details including progress, completion status, and error information
|
|
787
698
|
- Use to track the progress of a solution deployment initiated with deploySolution
|
|
788
699
|
|
|
789
|
-
### Power Platform API (`window.powerplatformAPI`)
|
|
790
|
-
|
|
791
|
-
Generic HTTP client for Power Platform Admin APIs covering all categories:
|
|
792
|
-
|
|
793
|
-
#### Category Methods
|
|
794
|
-
|
|
795
|
-
Each category (Analytics, AppManagement, Authorization, Connectivity, CopilotStudio, Dynamics, EnvironmentManagement, Governance, Licensing, PowerApps, PowerAutomate, PowerPages, ResourceQuery, UserManagement, WorkflowAgents) exposes:
|
|
796
|
-
|
|
797
|
-
- **Get(path?, connectionTarget?, headers?)**: Promise<PowerPlatformResponse>
|
|
798
|
-
- Makes a GET request to the category endpoint
|
|
799
|
-
- Example: `powerplatformAPI.PowerApps.Get('environments/{environmentId}/apps/{app}?api-version=2024-10-01')`
|
|
800
|
-
|
|
801
|
-
- **Post(path?, body?, connectionTarget?, headers?)**: Promise<PowerPlatformResponse>
|
|
802
|
-
- Makes a POST request to the category endpoint
|
|
803
|
-
|
|
804
|
-
- **Put(path?, body?, connectionTarget?, headers?)**: Promise<PowerPlatformResponse>
|
|
805
|
-
- Makes a PUT request to the category endpoint
|
|
806
|
-
|
|
807
|
-
- **Patch(path?, body?, connectionTarget?, headers?)**: Promise<PowerPlatformResponse>
|
|
808
|
-
- Makes a PATCH request to the category endpoint
|
|
809
|
-
|
|
810
|
-
- **Delete(path?, connectionTarget?, headers?, body?)**: Promise<PowerPlatformResponse>
|
|
811
|
-
- Makes a DELETE request to the category endpoint
|
|
812
|
-
|
|
813
|
-
Available categories and their base URLs:
|
|
814
|
-
|
|
815
|
-
| Category | Base URL |
|
|
816
|
-
| --------------------- | ----------------------------------------------------- |
|
|
817
|
-
| Analytics | `https://api.powerplatform.com/analytics` |
|
|
818
|
-
| AppManagement | `https://api.powerplatform.com/appmanagement` |
|
|
819
|
-
| Authorization | `https://api.powerplatform.com/authorization` |
|
|
820
|
-
| Connectivity | `https://api.powerplatform.com/connectivity` |
|
|
821
|
-
| CopilotStudio | `https://api.powerplatform.com/copilotstudio` |
|
|
822
|
-
| Dynamics | `https://api.powerplatform.com/dynamics` |
|
|
823
|
-
| EnvironmentManagement | `https://api.powerplatform.com/environmentmanagement` |
|
|
824
|
-
| Governance | `https://api.powerplatform.com/governance` |
|
|
825
|
-
| Licensing | `https://api.powerplatform.com/licensing` |
|
|
826
|
-
| PowerApps | `https://api.powerplatform.com/powerapps` |
|
|
827
|
-
| PowerAutomate | `https://api.powerplatform.com/powerautomate` |
|
|
828
|
-
| PowerPages | `https://api.powerplatform.com/powerpages` |
|
|
829
|
-
| ResourceQuery | `https://api.powerplatform.com/resourcequery` |
|
|
830
|
-
| UserManagement | `https://api.powerplatform.com/usermanagement` |
|
|
831
|
-
| WorkflowAgents | `https://api.powerplatform.com/workflowagents` |
|
|
832
|
-
|
|
833
700
|
### Security Notes
|
|
834
701
|
|
|
835
702
|
- **Access Tokens**: Tools do NOT have direct access to access tokens. All Dataverse operations are authenticated automatically by the platform.
|
package/bin/pptb-validate.js
CHANGED
|
@@ -1,244 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
"use strict";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* pptb-validate - CLI tool for validating Power Platform ToolBox tool packages
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* pptb-validate [options] [path/to/package.json]
|
|
10
|
-
*
|
|
11
|
-
* Options:
|
|
12
|
-
* --skip-url-checks Skip URL accessibility checks (faster, offline-friendly)
|
|
13
|
-
* --json Output results as JSON
|
|
14
|
-
* --help, -h Show help information
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const fs = require("fs");
|
|
18
|
-
const path = require("path");
|
|
19
|
-
const { validatePackageJson, validatePPTBConfig, KNOWN_CAPABILITY_TAGS } = require("../lib/validate");
|
|
20
|
-
|
|
21
|
-
// ANSI colour helpers – gracefully degrade when colours are unsupported
|
|
22
|
-
const NO_COLOR = !process.stdout.isTTY || process.env.NO_COLOR;
|
|
23
|
-
const c = {
|
|
24
|
-
red: (s) => (NO_COLOR ? s : `\x1b[31m${s}\x1b[0m`),
|
|
25
|
-
yellow: (s) => (NO_COLOR ? s : `\x1b[33m${s}\x1b[0m`),
|
|
26
|
-
green: (s) => (NO_COLOR ? s : `\x1b[32m${s}\x1b[0m`),
|
|
27
|
-
cyan: (s) => (NO_COLOR ? s : `\x1b[36m${s}\x1b[0m`),
|
|
28
|
-
bold: (s) => (NO_COLOR ? s : `\x1b[1m${s}\x1b[0m`),
|
|
29
|
-
dim: (s) => (NO_COLOR ? s : `\x1b[2m${s}\x1b[0m`),
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
function printHelp() {
|
|
33
|
-
console.log(`
|
|
34
|
-
${c.bold("pptb-validate")} – Power Platform ToolBox tool validator
|
|
35
|
-
|
|
36
|
-
${c.bold("USAGE")}
|
|
37
|
-
pptb-validate [options] [path/to/package.json]
|
|
38
|
-
|
|
39
|
-
When no path is given the tool looks for ${c.cyan("package.json")} in the current
|
|
40
|
-
working directory. If a ${c.cyan("pptb.config.json")} file exists in the same directory
|
|
41
|
-
it is automatically validated as well.
|
|
42
|
-
|
|
43
|
-
${c.bold("OPTIONS")}
|
|
44
|
-
${c.cyan("--skip-url-checks")} Skip URL reachability checks (faster, works offline)
|
|
45
|
-
${c.cyan("--json")} Print results as a JSON object instead of human-readable text
|
|
46
|
-
${c.cyan("--help")}, ${c.cyan("-h")} Show this help message
|
|
47
|
-
|
|
48
|
-
${c.bold("ADD TO YOUR TOOL'S package.json")}
|
|
49
|
-
${c.dim(`"scripts": {
|
|
50
|
-
"validate": "pptb-validate"
|
|
51
|
-
}`)}
|
|
52
|
-
|
|
53
|
-
Then run: ${c.cyan("npm run validate")}
|
|
54
|
-
|
|
55
|
-
${c.bold("EXAMPLES")}
|
|
56
|
-
npm run validate
|
|
57
|
-
npm run validate ./my-tool/package.json
|
|
58
|
-
npm run validate --skip-url-checks
|
|
59
|
-
npm run validate --json
|
|
60
|
-
`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function main() {
|
|
64
|
-
const args = process.argv.slice(2);
|
|
65
|
-
|
|
66
|
-
if (args.includes("--help") || args.includes("-h")) {
|
|
67
|
-
printHelp();
|
|
68
|
-
process.exit(0);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const skipUrlChecks = args.includes("--skip-url-checks");
|
|
72
|
-
const jsonOutput = args.includes("--json");
|
|
73
|
-
|
|
74
|
-
// Find the package.json path from positional args (skip flags)
|
|
75
|
-
const positional = args.filter((a) => !a.startsWith("-"));
|
|
76
|
-
let packageJsonPath = positional[0] || path.join(process.cwd(), "package.json");
|
|
77
|
-
|
|
78
|
-
// Resolve to absolute path
|
|
79
|
-
if (!path.isAbsolute(packageJsonPath)) {
|
|
80
|
-
packageJsonPath = path.resolve(process.cwd(), packageJsonPath);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Derive pptb.config.json path from the same directory as package.json
|
|
84
|
-
const toolDir = path.dirname(packageJsonPath);
|
|
85
|
-
const pptbConfigPath = path.join(toolDir, "pptb.config.json");
|
|
86
|
-
|
|
87
|
-
// --- Load package.json ---
|
|
88
|
-
if (!fs.existsSync(packageJsonPath)) {
|
|
89
|
-
if (jsonOutput) {
|
|
90
|
-
console.log(JSON.stringify({ valid: false, errors: [`package.json not found at: ${packageJsonPath}`], warnings: [] }, null, 2));
|
|
91
|
-
} else {
|
|
92
|
-
console.error(c.red(`✖ package.json not found at: ${packageJsonPath}`));
|
|
93
|
-
}
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
let packageJson;
|
|
98
|
-
try {
|
|
99
|
-
packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
100
|
-
} catch (err) {
|
|
101
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
102
|
-
if (jsonOutput) {
|
|
103
|
-
console.log(JSON.stringify({ valid: false, errors: [`Failed to parse package.json: ${message}`], warnings: [] }, null, 2));
|
|
104
|
-
} else {
|
|
105
|
-
console.error(c.red(`✖ Failed to parse package.json: ${message}`));
|
|
106
|
-
}
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// --- Load pptb.config.json (optional) ---
|
|
111
|
-
let pptbConfig = null;
|
|
112
|
-
let pptbConfigParseError = null;
|
|
113
|
-
if (fs.existsSync(pptbConfigPath)) {
|
|
114
|
-
try {
|
|
115
|
-
pptbConfig = JSON.parse(fs.readFileSync(pptbConfigPath, "utf8"));
|
|
116
|
-
} catch (err) {
|
|
117
|
-
pptbConfigParseError = err instanceof Error ? err.message : String(err);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// --- Run validation ---
|
|
122
|
-
if (!jsonOutput) {
|
|
123
|
-
console.log();
|
|
124
|
-
console.log(c.bold("Power Platform ToolBox – Tool Validator"));
|
|
125
|
-
console.log(c.dim("─".repeat(45)));
|
|
126
|
-
console.log(c.dim(`File: ${packageJsonPath}`));
|
|
127
|
-
if (pptbConfig !== null) {
|
|
128
|
-
console.log(c.dim(`Config: ${pptbConfigPath}`));
|
|
129
|
-
}
|
|
130
|
-
if (skipUrlChecks) {
|
|
131
|
-
console.log(c.yellow("⚠ URL reachability checks are skipped"));
|
|
132
|
-
}
|
|
133
|
-
console.log();
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
let packageResult;
|
|
137
|
-
try {
|
|
138
|
-
packageResult = await validatePackageJson(packageJson, { skipUrlChecks });
|
|
139
|
-
} catch (err) {
|
|
140
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
141
|
-
if (jsonOutput) {
|
|
142
|
-
console.log(JSON.stringify({ valid: false, errors: [`Unexpected validation error: ${message}`], warnings: [] }, null, 2));
|
|
143
|
-
} else {
|
|
144
|
-
console.error(c.red(`✖ Unexpected validation error: ${message}`));
|
|
145
|
-
}
|
|
146
|
-
process.exit(1);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// --- Validate pptb.config.json if present ---
|
|
150
|
-
let configResult = null;
|
|
151
|
-
if (pptbConfigParseError !== null) {
|
|
152
|
-
configResult = { valid: false, errors: [`Failed to parse pptb.config.json: ${pptbConfigParseError}`], warnings: [] };
|
|
153
|
-
} else if (pptbConfig !== null) {
|
|
154
|
-
configResult = validatePPTBConfig(pptbConfig);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Merge results for overall pass/fail
|
|
158
|
-
const allErrors = [...packageResult.errors, ...(configResult ? configResult.errors : [])];
|
|
159
|
-
const allWarnings = [...packageResult.warnings, ...(configResult ? configResult.warnings : [])];
|
|
160
|
-
const overallValid = packageResult.valid && (configResult === null || configResult.valid);
|
|
161
|
-
|
|
162
|
-
// --- Output results ---
|
|
163
|
-
if (jsonOutput) {
|
|
164
|
-
const output = {
|
|
165
|
-
valid: overallValid,
|
|
166
|
-
errors: allErrors,
|
|
167
|
-
warnings: allWarnings,
|
|
168
|
-
packageInfo: packageResult.packageInfo,
|
|
169
|
-
configInfo: configResult ? configResult.packageInfo : undefined,
|
|
170
|
-
};
|
|
171
|
-
console.log(JSON.stringify(output, null, 2));
|
|
172
|
-
process.exit(overallValid ? 0 : 1);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Human-readable output – package.json section
|
|
176
|
-
if (packageResult.errors.length > 0) {
|
|
177
|
-
console.log(c.bold(c.red(`package.json Errors (${packageResult.errors.length})`)));
|
|
178
|
-
packageResult.errors.forEach((e) => console.log(` ${c.red("✖")} ${e}`));
|
|
179
|
-
console.log();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (packageResult.warnings.length > 0) {
|
|
183
|
-
console.log(c.bold(c.yellow(`package.json Warnings (${packageResult.warnings.length})`)));
|
|
184
|
-
packageResult.warnings.forEach((w) => console.log(` ${c.yellow("⚠")} ${w}`));
|
|
185
|
-
console.log();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Human-readable output – pptb.config.json section
|
|
189
|
-
if (configResult !== null) {
|
|
190
|
-
if (configResult.errors.length > 0) {
|
|
191
|
-
console.log(c.bold(c.red(`pptb.config.json Errors (${configResult.errors.length})`)));
|
|
192
|
-
configResult.errors.forEach((e) => console.log(` ${c.red("✖")} ${e}`));
|
|
193
|
-
console.log();
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (configResult.warnings.length > 0) {
|
|
197
|
-
console.log(c.bold(c.yellow(`pptb.config.json Warnings (${configResult.warnings.length})`)));
|
|
198
|
-
configResult.warnings.forEach((w) => console.log(` ${c.yellow("⚠")} ${w}`));
|
|
199
|
-
console.log();
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (overallValid) {
|
|
204
|
-
const info = packageResult.packageInfo;
|
|
205
|
-
console.log(c.green(c.bold("✔ Validation passed")));
|
|
206
|
-
console.log();
|
|
207
|
-
console.log(c.bold("Package summary"));
|
|
208
|
-
console.log(c.dim("─".repeat(45)));
|
|
209
|
-
console.log(` Name : ${info.name}`);
|
|
210
|
-
console.log(` Version : ${info.version}`);
|
|
211
|
-
console.log(` Display name: ${info.displayName}`);
|
|
212
|
-
console.log(` Description : ${info.description}`);
|
|
213
|
-
console.log(` License : ${info.license}`);
|
|
214
|
-
console.log(` Contributors: ${info.contributors.map((contributor) => contributor.name).join(", ")}`);
|
|
215
|
-
if (info.icon) {
|
|
216
|
-
console.log(` Icon : ${info.icon}`);
|
|
217
|
-
}
|
|
218
|
-
if (info.features) {
|
|
219
|
-
console.log(` Features : multiConnection=${info.features.multiConnection}${info.features.minAPI ? `, minAPI=${info.features.minAPI}` : ""}`);
|
|
220
|
-
}
|
|
221
|
-
if (configResult !== null && configResult.packageInfo && configResult.packageInfo.invocation) {
|
|
222
|
-
const inv = configResult.packageInfo.invocation;
|
|
223
|
-
console.log(` Invocation : version=${inv.version}`);
|
|
224
|
-
if (Array.isArray(inv.capabilities) && inv.capabilities.length > 0) {
|
|
225
|
-
console.log(` Capabilities: ${inv.capabilities.join(", ")}`);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
console.log();
|
|
229
|
-
console.log(c.dim(`Known capability tags: ${KNOWN_CAPABILITY_TAGS.join(", ")}`));
|
|
230
|
-
console.log();
|
|
231
|
-
} else {
|
|
232
|
-
console.log(c.red(c.bold("✖ Validation failed")));
|
|
233
|
-
console.log();
|
|
234
|
-
console.log(c.dim("Fix the errors listed above and re-run pptb-validate before publishing."));
|
|
235
|
-
console.log();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
process.exit(overallValid ? 0 : 1);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
main().catch((err) => {
|
|
242
|
-
console.error(c.red(`✖ Fatal error: ${err instanceof Error ? err.message : String(err)}`));
|
|
243
|
-
process.exit(1);
|
|
244
|
-
});
|
|
2
|
+
require("@pptb/validate/cli");
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/triple-slash-reference */
|
|
2
1
|
/**
|
|
3
2
|
* Power Platform ToolBox API Type Definitions
|
|
4
3
|
*
|
|
@@ -17,11 +16,9 @@
|
|
|
17
16
|
|
|
18
17
|
/// <reference path="./toolboxAPI.d.ts" />
|
|
19
18
|
/// <reference path="./dataverseAPI.d.ts" />
|
|
20
|
-
/// <reference path="./powerplatformAPI.d.ts" />
|
|
21
19
|
/// <reference path="./pptbConfig.d.ts" />
|
|
22
20
|
|
|
23
21
|
// Re-export all namespaces for convenience
|
|
24
22
|
export * from "./dataverseAPI";
|
|
25
|
-
export * from "./powerplatformAPI";
|
|
26
|
-
export * from "./pptbConfig";
|
|
27
23
|
export * from "./toolboxAPI";
|
|
24
|
+
export * from "./pptbConfig";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pptb/types",
|
|
3
|
-
"version": "1.2.4-beta.
|
|
3
|
+
"version": "1.2.4-beta.2",
|
|
4
4
|
"description": "Type definitions for Power Platform ToolBox APIs and validity checks for tool packages",
|
|
5
5
|
"main": "index.d.ts",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -22,14 +22,15 @@
|
|
|
22
22
|
"bin": {
|
|
23
23
|
"pptb-validate": "./bin/pptb-validate.js"
|
|
24
24
|
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@pptb/validate": "^0.0.1"
|
|
27
|
+
},
|
|
25
28
|
"files": [
|
|
26
29
|
"index.d.ts",
|
|
27
30
|
"toolboxAPI.d.ts",
|
|
28
31
|
"dataverseAPI.d.ts",
|
|
29
|
-
"powerplatformAPI.d.ts",
|
|
30
32
|
"pptbConfig.d.ts",
|
|
31
33
|
"bin/",
|
|
32
|
-
"lib/",
|
|
33
34
|
"README.md"
|
|
34
35
|
],
|
|
35
36
|
"scripts": {
|
package/lib/validate.js
DELETED
|
@@ -1,486 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Tool validation logic for Power Platform ToolBox tools.
|
|
6
|
-
* Mirrors the validation rules used during the official review process.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/** @typedef {{ name: string; url?: string }} Contributor */
|
|
10
|
-
/** @typedef {{ "connect-src"?: string[]; "script-src"?: string[]; "style-src"?: string[]; "img-src"?: string[]; "font-src"?: string[]; "frame-src"?: string[]; "media-src"?: string[] }} CspExceptions */
|
|
11
|
-
/** @typedef {{ repository?: string; website?: string; funding?: string; readmeUrl?: string }} Configurations */
|
|
12
|
-
/** @typedef {{ multiConnection?: "required" | "optional" | "none"; minAPI?: string }} Features */
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {{
|
|
15
|
-
* name: string;
|
|
16
|
-
* version: string;
|
|
17
|
-
* displayName?: string;
|
|
18
|
-
* description?: string;
|
|
19
|
-
* contributors?: Contributor[];
|
|
20
|
-
* cspExceptions?: CspExceptions;
|
|
21
|
-
* license?: string;
|
|
22
|
-
* icon?: string;
|
|
23
|
-
* configurations?: Configurations;
|
|
24
|
-
* features?: Features;
|
|
25
|
-
* }} ToolPackageJson
|
|
26
|
-
*/
|
|
27
|
-
/**
|
|
28
|
-
* @typedef {{
|
|
29
|
-
* valid: boolean;
|
|
30
|
-
* errors: string[];
|
|
31
|
-
* warnings: string[];
|
|
32
|
-
* packageInfo?: object;
|
|
33
|
-
* }} ValidationResult
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @typedef {{ type?: string; enum?: string[]; items?: object }} JsonSchemaProperty
|
|
38
|
-
* @typedef {{ properties?: Record<string, JsonSchemaProperty> }} JsonSchemaObject
|
|
39
|
-
* @typedef {{
|
|
40
|
-
* version: string;
|
|
41
|
-
* prefill?: JsonSchemaObject;
|
|
42
|
-
* returnTopic?: JsonSchemaObject;
|
|
43
|
-
* }} InvocationConfig
|
|
44
|
-
* @typedef {{
|
|
45
|
-
* invocation?: InvocationConfig;
|
|
46
|
-
* }} PPTBConfig
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
// List of approved open source licenses
|
|
50
|
-
const APPROVED_LICENSES = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "GPL-2.0", "GPL-3.0", "LGPL-3.0", "ISC", "AGPL-3.0-only"];
|
|
51
|
-
|
|
52
|
-
// Valid multiConnection values
|
|
53
|
-
const VALID_MULTI_CONNECTION_VALUES = ["required", "optional", "none"];
|
|
54
|
-
|
|
55
|
-
// Semver regex for minAPI validation
|
|
56
|
-
const SEMVER_REGEX = /^\d+\.\d+\.\d+(-[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?(\+[0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*)?$/;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Built-in list of well-known capability tags.
|
|
60
|
-
* Mirrors the entries in the Supabase `capability_tags` table.
|
|
61
|
-
* New tags are added to Supabase first (configurable without a deploy) and are
|
|
62
|
-
* then reflected here in the next package release so offline validation stays current.
|
|
63
|
-
*
|
|
64
|
-
* When a tool declares a tag that is not in this list the validator emits a warning
|
|
65
|
-
* (not an error) so existing tools are not broken by registry updates.
|
|
66
|
-
*
|
|
67
|
-
* **Keep in sync with `BUILT_IN_CAPABILITY_TAGS` in
|
|
68
|
-
* `src/main/managers/toolRegistryManager.ts`.** Because this file is a standalone
|
|
69
|
-
* Node.js module that cannot import from the Electron/TypeScript source tree, both
|
|
70
|
-
* lists must be updated together whenever a new tag is added.
|
|
71
|
-
*/
|
|
72
|
-
const KNOWN_CAPABILITY_TAGS = [
|
|
73
|
-
"fetchxml",
|
|
74
|
-
"entity-picker",
|
|
75
|
-
"record-selector",
|
|
76
|
-
"solution-selector",
|
|
77
|
-
"webresource-editor",
|
|
78
|
-
"plugin-inspector",
|
|
79
|
-
"pcf-control-builder",
|
|
80
|
-
];
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Checks if a string is a valid URL.
|
|
84
|
-
* @param {string} url
|
|
85
|
-
* @returns {boolean}
|
|
86
|
-
*/
|
|
87
|
-
function isValidUrl(url) {
|
|
88
|
-
try {
|
|
89
|
-
new URL(url);
|
|
90
|
-
return true;
|
|
91
|
-
} catch {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Checks if a URL hostname is a GitHub domain.
|
|
98
|
-
* @param {string} url
|
|
99
|
-
* @returns {boolean}
|
|
100
|
-
*/
|
|
101
|
-
function isGithubDomain(url) {
|
|
102
|
-
try {
|
|
103
|
-
const hostname = new URL(url).hostname.toLowerCase();
|
|
104
|
-
return hostname === "github.com" || hostname.endsWith(".github.com");
|
|
105
|
-
} catch {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Validates an icon path string.
|
|
112
|
-
* @param {string} fieldName
|
|
113
|
-
* @param {string} iconPath
|
|
114
|
-
* @param {string[]} errors
|
|
115
|
-
*/
|
|
116
|
-
function validateIconPath(fieldName, iconPath, errors) {
|
|
117
|
-
if (iconPath.startsWith("http://") || iconPath.startsWith("https://")) {
|
|
118
|
-
errors.push(`${fieldName} cannot be an HTTP(S) URL - icons must be bundled under dist`);
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (iconPath.startsWith("/")) {
|
|
122
|
-
errors.push(`${fieldName} must be a relative path (e.g., 'icon.svg' or 'icons/icon.svg')`);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
// Reject Windows absolute paths like "C:\..." or "C:/"
|
|
126
|
-
if (/^[a-zA-Z]:/.test(iconPath)) {
|
|
127
|
-
errors.push(`${fieldName} must be a relative path (e.g., 'icon.svg' or 'icons/icon.svg')`);
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
// Reject backslashes — icon paths must use forward slashes
|
|
131
|
-
if (iconPath.includes("\\")) {
|
|
132
|
-
errors.push(`${fieldName} must use forward slashes, not backslashes (e.g., 'icons/icon.svg')`);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
if (iconPath.includes("..")) {
|
|
136
|
-
errors.push(`${fieldName} cannot contain '..' (path traversal not allowed)`);
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
if (!iconPath.toLowerCase().endsWith(".svg")) {
|
|
140
|
-
errors.push(`${fieldName} must be an SVG file with .svg extension`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Checks if a URL is accessible by making a HEAD request.
|
|
146
|
-
* @param {string} url
|
|
147
|
-
* @returns {Promise<boolean>}
|
|
148
|
-
*/
|
|
149
|
-
async function isUrlAccessible(url) {
|
|
150
|
-
try {
|
|
151
|
-
const response = await fetch(url, { method: "HEAD", redirect: "follow" });
|
|
152
|
-
return response.ok;
|
|
153
|
-
} catch {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Validates a tool's package.json against the official review criteria.
|
|
160
|
-
*
|
|
161
|
-
* @param {ToolPackageJson} packageJson - The parsed package.json object.
|
|
162
|
-
* @param {{ skipUrlChecks?: boolean }} [options] - Validation options.
|
|
163
|
-
* @returns {Promise<ValidationResult>}
|
|
164
|
-
*/
|
|
165
|
-
async function validatePackageJson(packageJson, options = {}) {
|
|
166
|
-
const { skipUrlChecks = false } = options;
|
|
167
|
-
const errors = /** @type {string[]} */ ([]);
|
|
168
|
-
const warnings = /** @type {string[]} */ ([]);
|
|
169
|
-
|
|
170
|
-
// Required fields
|
|
171
|
-
if (!packageJson.name || typeof packageJson.name !== "string") {
|
|
172
|
-
errors.push("Package name is required and must be a string");
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (!packageJson.version || typeof packageJson.version !== "string") {
|
|
176
|
-
errors.push("Package version is required and must be a string");
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
if (!packageJson.displayName || typeof packageJson.displayName !== "string") {
|
|
180
|
-
errors.push("displayName is required and must be a string");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (!packageJson.description || typeof packageJson.description !== "string") {
|
|
184
|
-
errors.push("description is required and must be a string");
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// License validation
|
|
188
|
-
if (!packageJson.license) {
|
|
189
|
-
errors.push("license is required");
|
|
190
|
-
} else if (!APPROVED_LICENSES.includes(packageJson.license)) {
|
|
191
|
-
errors.push(`License "${packageJson.license}" is not in the approved list. Approved licenses: ${APPROVED_LICENSES.join(", ")}`);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Icon validation (optional, but must be a relative SVG path if provided)
|
|
195
|
-
if (packageJson.icon === undefined || packageJson.icon === null) {
|
|
196
|
-
warnings.push("icon is not set; consider adding a bundled SVG icon so your tool displays properly in the marketplace");
|
|
197
|
-
} else if (typeof packageJson.icon !== "string") {
|
|
198
|
-
errors.push("icon must be a string (relative path to bundled SVG under dist)");
|
|
199
|
-
} else {
|
|
200
|
-
validateIconPath("icon", packageJson.icon, errors);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Contributors validation
|
|
204
|
-
if (!packageJson.contributors || !Array.isArray(packageJson.contributors)) {
|
|
205
|
-
errors.push("contributors is required and must be an array");
|
|
206
|
-
} else if (packageJson.contributors.length === 0) {
|
|
207
|
-
errors.push("At least one contributor is required");
|
|
208
|
-
} else {
|
|
209
|
-
packageJson.contributors.forEach((contributor, index) => {
|
|
210
|
-
if (!contributor.name || typeof contributor.name !== "string") {
|
|
211
|
-
errors.push(`Contributor at index ${index} must have a name`);
|
|
212
|
-
}
|
|
213
|
-
if (contributor.url && !isValidUrl(contributor.url)) {
|
|
214
|
-
warnings.push(`Contributor "${contributor.name}" has an invalid URL`);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Configurations validation
|
|
220
|
-
if (!packageJson.configurations || typeof packageJson.configurations !== "object") {
|
|
221
|
-
errors.push("configurations is required and must include repository and readmeUrl");
|
|
222
|
-
} else {
|
|
223
|
-
const configs = packageJson.configurations;
|
|
224
|
-
|
|
225
|
-
// configurations.iconUrl is no longer supported
|
|
226
|
-
if (/** @type {Record<string, unknown>} */ (configs).iconUrl !== undefined) {
|
|
227
|
-
errors.push("configurations.iconUrl is no longer supported; use top-level 'icon' for bundled SVG path");
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Repository validation
|
|
231
|
-
if (!configs.repository || typeof configs.repository !== "string") {
|
|
232
|
-
errors.push("configurations.repository is required and must be a URL");
|
|
233
|
-
} else if (!isValidUrl(configs.repository)) {
|
|
234
|
-
errors.push("configurations.repository has an invalid URL format");
|
|
235
|
-
} else if (!skipUrlChecks) {
|
|
236
|
-
const accessible = await isUrlAccessible(configs.repository);
|
|
237
|
-
if (!accessible) {
|
|
238
|
-
errors.push("configurations.repository URL is not accessible");
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Website validation (optional but recommended)
|
|
243
|
-
if (!configs.website) {
|
|
244
|
-
warnings.push("configurations.website is not set; consider adding a URL where users can learn more about your tool");
|
|
245
|
-
} else if (!isValidUrl(configs.website)) {
|
|
246
|
-
warnings.push("configurations.website has an invalid URL format");
|
|
247
|
-
} else if (!skipUrlChecks) {
|
|
248
|
-
const accessible = await isUrlAccessible(configs.website);
|
|
249
|
-
if (!accessible) {
|
|
250
|
-
warnings.push("configurations.website URL is not accessible");
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Funding validation (optional but recommended)
|
|
255
|
-
if (configs.funding) {
|
|
256
|
-
if (!isValidUrl(configs.funding)) {
|
|
257
|
-
warnings.push("configurations.funding has an invalid URL format");
|
|
258
|
-
} else if (!skipUrlChecks) {
|
|
259
|
-
const accessible = await isUrlAccessible(configs.funding);
|
|
260
|
-
if (!accessible) {
|
|
261
|
-
warnings.push("configurations.funding URL is not accessible");
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
// ReadmeUrl validation
|
|
267
|
-
if (!configs.readmeUrl || typeof configs.readmeUrl !== "string") {
|
|
268
|
-
errors.push("configurations.readmeUrl is required and must be a URL");
|
|
269
|
-
} else if (!isValidUrl(configs.readmeUrl)) {
|
|
270
|
-
errors.push("configurations.readmeUrl has an invalid URL format");
|
|
271
|
-
} else if (isGithubDomain(configs.readmeUrl)) {
|
|
272
|
-
errors.push("configurations.readmeUrl cannot be hosted on github.com; use raw.githubusercontent.com or another domain");
|
|
273
|
-
} else if (!skipUrlChecks) {
|
|
274
|
-
const accessible = await isUrlAccessible(configs.readmeUrl);
|
|
275
|
-
if (!accessible) {
|
|
276
|
-
errors.push("configurations.readmeUrl is not accessible");
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// CSP Exceptions validation (optional, but validated if present)
|
|
282
|
-
if (packageJson.cspExceptions) {
|
|
283
|
-
const cspExceptions = packageJson.cspExceptions;
|
|
284
|
-
|
|
285
|
-
if (typeof cspExceptions !== "object" || cspExceptions === null || Array.isArray(cspExceptions)) {
|
|
286
|
-
errors.push("cspExceptions must be an object mapping CSP directives to arrays of strings");
|
|
287
|
-
} else {
|
|
288
|
-
const validCspDirectives = ["connect-src", "script-src", "style-src", "img-src", "font-src", "frame-src", "media-src"];
|
|
289
|
-
|
|
290
|
-
const hasAnyDirectives = Object.keys(cspExceptions).length > 0;
|
|
291
|
-
if (!hasAnyDirectives) {
|
|
292
|
-
errors.push("cspExceptions cannot be empty. If CSP exceptions are not needed, remove the cspExceptions field");
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
Object.keys(cspExceptions).forEach((directive) => {
|
|
296
|
-
if (!validCspDirectives.includes(directive)) {
|
|
297
|
-
warnings.push(`Unknown CSP directive: ${directive}`);
|
|
298
|
-
}
|
|
299
|
-
const values = cspExceptions[/** @type {keyof CspExceptions} */ (directive)];
|
|
300
|
-
if (values && !Array.isArray(values)) {
|
|
301
|
-
errors.push(`CSP directive "${directive}" must be an array of strings`);
|
|
302
|
-
} else if (values && values.length === 0) {
|
|
303
|
-
errors.push(`CSP directive "${directive}" cannot be an empty array`);
|
|
304
|
-
}
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Features validation (optional, but validated if present)
|
|
310
|
-
if (packageJson.features !== undefined) {
|
|
311
|
-
const features = packageJson.features;
|
|
312
|
-
|
|
313
|
-
if (features === null || typeof features !== "object" || Array.isArray(features)) {
|
|
314
|
-
errors.push("features must be a non-array object with optional 'multiConnection' and 'minAPI' properties");
|
|
315
|
-
} else {
|
|
316
|
-
const VALID_FEATURE_KEYS = ["multiConnection", "minAPI"];
|
|
317
|
-
const featureKeys = Object.keys(features);
|
|
318
|
-
const invalidKeys = featureKeys.filter((key) => !VALID_FEATURE_KEYS.includes(key));
|
|
319
|
-
|
|
320
|
-
if (invalidKeys.length > 0) {
|
|
321
|
-
errors.push(`features can only contain ${VALID_FEATURE_KEYS.map((k) => `'${k}'`).join(", ")} properties. Invalid properties: ${invalidKeys.join(", ")}`);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (features.multiConnection === undefined) {
|
|
325
|
-
errors.push("features.multiConnection is required when features object is provided");
|
|
326
|
-
} else if (!VALID_MULTI_CONNECTION_VALUES.includes(features.multiConnection)) {
|
|
327
|
-
errors.push(`features.multiConnection must be one of: ${VALID_MULTI_CONNECTION_VALUES.join(", ")}`);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (features.minAPI !== undefined) {
|
|
331
|
-
if (typeof features.minAPI !== "string" || !SEMVER_REGEX.test(features.minAPI)) {
|
|
332
|
-
errors.push("features.minAPI must be a valid semantic version string (e.g., '1.0.0')");
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const valid = errors.length === 0;
|
|
339
|
-
|
|
340
|
-
return {
|
|
341
|
-
valid,
|
|
342
|
-
errors,
|
|
343
|
-
warnings,
|
|
344
|
-
packageInfo: valid
|
|
345
|
-
? {
|
|
346
|
-
name: packageJson.name,
|
|
347
|
-
version: packageJson.version,
|
|
348
|
-
displayName: packageJson.displayName,
|
|
349
|
-
description: packageJson.description,
|
|
350
|
-
license: packageJson.license,
|
|
351
|
-
contributors: packageJson.contributors,
|
|
352
|
-
cspExceptions: packageJson.cspExceptions,
|
|
353
|
-
icon: packageJson.icon,
|
|
354
|
-
configurations: packageJson.configurations,
|
|
355
|
-
features: packageJson.features,
|
|
356
|
-
}
|
|
357
|
-
: undefined,
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
module.exports = { validatePackageJson, validatePPTBConfig, isValidUrl, APPROVED_LICENSES, KNOWN_CAPABILITY_TAGS };
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* Validates a tool's pptb.config.json against the official review criteria.
|
|
365
|
-
*
|
|
366
|
-
* @param {PPTBConfig} config - The parsed pptb.config.json object.
|
|
367
|
-
* @returns {ValidationResult}
|
|
368
|
-
*/
|
|
369
|
-
function validatePPTBConfig(config) {
|
|
370
|
-
const errors = /** @type {string[]} */ ([]);
|
|
371
|
-
const warnings = /** @type {string[]} */ ([]);
|
|
372
|
-
|
|
373
|
-
if (config === null || typeof config !== "object" || Array.isArray(config)) {
|
|
374
|
-
errors.push("pptb.config.json must be a JSON object");
|
|
375
|
-
return { valid: false, errors, warnings };
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const VALID_ROOT_KEYS = ["invocation"];
|
|
379
|
-
const unknownRootKeys = Object.keys(config).filter((k) => !VALID_ROOT_KEYS.includes(k));
|
|
380
|
-
if (unknownRootKeys.length > 0) {
|
|
381
|
-
warnings.push(`pptb.config.json contains unrecognised root keys: ${unknownRootKeys.join(", ")}`);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Invocation section
|
|
385
|
-
if (config.invocation !== undefined) {
|
|
386
|
-
const inv = config.invocation;
|
|
387
|
-
|
|
388
|
-
if (inv === null || typeof inv !== "object" || Array.isArray(inv)) {
|
|
389
|
-
errors.push("invocation must be a non-array object");
|
|
390
|
-
} else {
|
|
391
|
-
// invocation.version – required, must be a valid semver string
|
|
392
|
-
if (inv.version === undefined || inv.version === null) {
|
|
393
|
-
errors.push("invocation.version is required");
|
|
394
|
-
} else if (typeof inv.version !== "string") {
|
|
395
|
-
errors.push("invocation.version must be a string");
|
|
396
|
-
} else if (!SEMVER_REGEX.test(inv.version)) {
|
|
397
|
-
errors.push(`invocation.version "${inv.version}" is not a valid semantic version string (e.g. "1.0.0")`);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// invocation.prefill – optional JSON-schema-like object
|
|
401
|
-
if (inv.prefill !== undefined) {
|
|
402
|
-
if (inv.prefill === null || typeof inv.prefill !== "object" || Array.isArray(inv.prefill)) {
|
|
403
|
-
errors.push("invocation.prefill must be a non-array object");
|
|
404
|
-
} else if (inv.prefill.properties !== undefined) {
|
|
405
|
-
validateJsonSchemaProperties("invocation.prefill", inv.prefill.properties, errors);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// invocation.returnTopic – optional JSON-schema-like object
|
|
410
|
-
if (inv.returnTopic !== undefined) {
|
|
411
|
-
if (inv.returnTopic === null || typeof inv.returnTopic !== "object" || Array.isArray(inv.returnTopic)) {
|
|
412
|
-
errors.push("invocation.returnTopic must be a non-array object");
|
|
413
|
-
} else if (inv.returnTopic.properties !== undefined) {
|
|
414
|
-
validateJsonSchemaProperties("invocation.returnTopic", inv.returnTopic.properties, errors);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// invocation.capabilities – optional array of non-empty strings
|
|
419
|
-
if (inv.capabilities !== undefined) {
|
|
420
|
-
if (!Array.isArray(inv.capabilities)) {
|
|
421
|
-
errors.push("invocation.capabilities must be an array");
|
|
422
|
-
} else {
|
|
423
|
-
inv.capabilities.forEach((cap, idx) => {
|
|
424
|
-
if (typeof cap !== "string" || cap.trim().length === 0) {
|
|
425
|
-
errors.push(`invocation.capabilities[${idx}] must be a non-empty string`);
|
|
426
|
-
} else if (!KNOWN_CAPABILITY_TAGS.includes(cap.trim())) {
|
|
427
|
-
warnings.push(
|
|
428
|
-
`invocation.capabilities[${idx}] "${cap}" is not a recognised capability tag. ` +
|
|
429
|
-
`Known tags: ${KNOWN_CAPABILITY_TAGS.join(", ")}. ` +
|
|
430
|
-
"If this is a new tag, ensure it has been added to the capability registry.",
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
const valid = errors.length === 0;
|
|
440
|
-
|
|
441
|
-
return {
|
|
442
|
-
valid,
|
|
443
|
-
errors,
|
|
444
|
-
warnings,
|
|
445
|
-
packageInfo: valid
|
|
446
|
-
? {
|
|
447
|
-
invocation: config.invocation,
|
|
448
|
-
}
|
|
449
|
-
: undefined,
|
|
450
|
-
};
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
/**
|
|
454
|
-
* Validates a JSON-schema-style `properties` map used inside invocation sections.
|
|
455
|
-
* Only performs basic structural validation; full JSON Schema validation is not required.
|
|
456
|
-
*
|
|
457
|
-
* @param {string} fieldName
|
|
458
|
-
* @param {unknown} properties
|
|
459
|
-
* @param {string[]} errors
|
|
460
|
-
*/
|
|
461
|
-
function validateJsonSchemaProperties(fieldName, properties, errors) {
|
|
462
|
-
if (properties === null || typeof properties !== "object" || Array.isArray(properties)) {
|
|
463
|
-
errors.push(`${fieldName}.properties must be a non-array object`);
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
const propsRecord = /** @type {Record<string, unknown>} */ (properties);
|
|
468
|
-
|
|
469
|
-
for (const [key, value] of Object.entries(propsRecord)) {
|
|
470
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
471
|
-
errors.push(`${fieldName}.properties.${key} must be an object`);
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
const prop = /** @type {Record<string, unknown>} */ (value);
|
|
475
|
-
if (prop.type !== undefined && typeof prop.type !== "string") {
|
|
476
|
-
errors.push(`${fieldName}.properties.${key}.type must be a string`);
|
|
477
|
-
}
|
|
478
|
-
if (prop.enum !== undefined) {
|
|
479
|
-
if (!Array.isArray(prop.enum)) {
|
|
480
|
-
errors.push(`${fieldName}.properties.${key}.enum must be an array`);
|
|
481
|
-
} else if (prop.enum.length === 0) {
|
|
482
|
-
errors.push(`${fieldName}.properties.${key}.enum must not be empty`);
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
}
|
package/powerplatformAPI.d.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Power Platform ToolBox - Power Platform API Type Definitions
|
|
3
|
-
*
|
|
4
|
-
* Power Platform Admin APIs exposed to tools via window.powerplatformAPI
|
|
5
|
-
* https://learn.microsoft.com/en-us/rest/api/power-platform/
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
declare namespace PowerPlatformAPI {
|
|
9
|
-
/**
|
|
10
|
-
* Power Platform API category response type
|
|
11
|
-
*/
|
|
12
|
-
export interface PowerPlatformResponse {
|
|
13
|
-
[key: string]: unknown;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Generic HTTP method type
|
|
18
|
-
*/
|
|
19
|
-
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Power Platform API category client interface
|
|
23
|
-
* Provides generic HTTP methods for each API category
|
|
24
|
-
*/
|
|
25
|
-
export interface CategoryClient {
|
|
26
|
-
/**
|
|
27
|
-
* Make a GET request to the category endpoint
|
|
28
|
-
* @param path Relative path after the category base URL (e.g., "environments/{environmentId}/apps/{app}?api-version=2024-10-01")
|
|
29
|
-
* @param connectionTarget Optional connection target for multi-connection tools ('primary' or 'secondary'). Defaults to 'primary'.
|
|
30
|
-
* @param headers Optional custom headers
|
|
31
|
-
*/
|
|
32
|
-
Get: (path?: string, connectionTarget?: "primary" | "secondary", headers?: Record<string, string>) => Promise<PowerPlatformResponse>;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Make a POST request to the category endpoint
|
|
36
|
-
* @param path Relative path after the category base URL
|
|
37
|
-
* @param body Optional request body
|
|
38
|
-
* @param connectionTarget Optional connection target for multi-connection tools ('primary' or 'secondary'). Defaults to 'primary'.
|
|
39
|
-
* @param headers Optional custom headers
|
|
40
|
-
*/
|
|
41
|
-
Post: (path?: string, body?: unknown, connectionTarget?: "primary" | "secondary", headers?: Record<string, string>) => Promise<PowerPlatformResponse>;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Make a PUT request to the category endpoint
|
|
45
|
-
* @param path Relative path after the category base URL
|
|
46
|
-
* @param body Optional request body
|
|
47
|
-
* @param connectionTarget Optional connection target for multi-connection tools ('primary' or 'secondary'). Defaults to 'primary'.
|
|
48
|
-
* @param headers Optional custom headers
|
|
49
|
-
*/
|
|
50
|
-
Put: (path?: string, body?: unknown, connectionTarget?: "primary" | "secondary", headers?: Record<string, string>) => Promise<PowerPlatformResponse>;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Make a PATCH request to the category endpoint
|
|
54
|
-
* @param path Relative path after the category base URL
|
|
55
|
-
* @param body Optional request body
|
|
56
|
-
* @param connectionTarget Optional connection target for multi-connection tools ('primary' or 'secondary'). Defaults to 'primary'.
|
|
57
|
-
* @param headers Optional custom headers
|
|
58
|
-
*/
|
|
59
|
-
Patch: (path?: string, body?: unknown, connectionTarget?: "primary" | "secondary", headers?: Record<string, string>) => Promise<PowerPlatformResponse>;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Make a DELETE request to the category endpoint
|
|
63
|
-
* @param path Relative path after the category base URL
|
|
64
|
-
* @param connectionTarget Optional connection target for multi-connection tools ('primary' or 'secondary'). Defaults to 'primary'.
|
|
65
|
-
* @param headers Optional custom headers
|
|
66
|
-
* @param body Optional request body (for DELETE with payload)
|
|
67
|
-
*/
|
|
68
|
-
Delete: (path?: string, connectionTarget?: "primary" | "secondary", headers?: Record<string, string>, body?: unknown) => Promise<PowerPlatformResponse>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Power Platform Analytics API
|
|
73
|
-
* https://api.powerplatform.com/analytics
|
|
74
|
-
*/
|
|
75
|
-
export interface Analytics extends CategoryClient {}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Power Platform App Management API
|
|
79
|
-
* https://api.powerplatform.com/appmanagement
|
|
80
|
-
*/
|
|
81
|
-
export interface AppManagement extends CategoryClient {}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Power Platform Authorization API
|
|
85
|
-
* https://api.powerplatform.com/authorization
|
|
86
|
-
*/
|
|
87
|
-
export interface Authorization extends CategoryClient {}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Power Platform Connectivity API
|
|
91
|
-
* https://api.powerplatform.com/connectivity
|
|
92
|
-
*/
|
|
93
|
-
export interface Connectivity extends CategoryClient {}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Power Platform Copilot Studio API
|
|
97
|
-
* https://api.powerplatform.com/copilotstudio
|
|
98
|
-
*/
|
|
99
|
-
export interface CopilotStudio extends CategoryClient {}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Power Platform Dynamics API
|
|
103
|
-
* https://api.powerplatform.com/dynamics
|
|
104
|
-
*/
|
|
105
|
-
export interface Dynamics extends CategoryClient {}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Power Platform Environment Management API
|
|
109
|
-
* https://api.powerplatform.com/environmentmanagement
|
|
110
|
-
*/
|
|
111
|
-
export interface EnvironmentManagement extends CategoryClient {}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Power Platform Governance API
|
|
115
|
-
* https://api.powerplatform.com/governance
|
|
116
|
-
*/
|
|
117
|
-
export interface Governance extends CategoryClient {}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Power Platform Licensing API
|
|
121
|
-
* https://api.powerplatform.com/licensing
|
|
122
|
-
*/
|
|
123
|
-
export interface Licensing extends CategoryClient {}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Power Platform Power Apps API
|
|
127
|
-
* https://api.powerplatform.com/powerapps
|
|
128
|
-
*/
|
|
129
|
-
export interface PowerApps extends CategoryClient {}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Power Platform Power Automate API
|
|
133
|
-
* https://api.powerplatform.com/powerautomate
|
|
134
|
-
*/
|
|
135
|
-
export interface PowerAutomate extends CategoryClient {}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Power Platform Power Pages API
|
|
139
|
-
* https://api.powerplatform.com/powerpages
|
|
140
|
-
*/
|
|
141
|
-
export interface PowerPages extends CategoryClient {}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Power Platform Resource Query API
|
|
145
|
-
* https://api.powerplatform.com/resourcequery
|
|
146
|
-
*/
|
|
147
|
-
export interface ResourceQuery extends CategoryClient {}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Power Platform User Management API
|
|
151
|
-
* https://api.powerplatform.com/usermanagement
|
|
152
|
-
*/
|
|
153
|
-
export interface UserManagement extends CategoryClient {}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Power Platform Workflow Agents API
|
|
157
|
-
* https://api.powerplatform.com/workflowagents
|
|
158
|
-
*/
|
|
159
|
-
export interface WorkflowAgents extends CategoryClient {}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Main Power Platform API interface
|
|
163
|
-
* Each category exposes generic HTTP methods (Get, Post, Put, Patch, Delete)
|
|
164
|
-
*/
|
|
165
|
-
export interface API {
|
|
166
|
-
Analytics: Analytics;
|
|
167
|
-
AppManagement: AppManagement;
|
|
168
|
-
Authorization: Authorization;
|
|
169
|
-
Connectivity: Connectivity;
|
|
170
|
-
CopilotStudio: CopilotStudio;
|
|
171
|
-
Dynamics: Dynamics;
|
|
172
|
-
EnvironmentManagement: EnvironmentManagement;
|
|
173
|
-
Governance: Governance;
|
|
174
|
-
Licensing: Licensing;
|
|
175
|
-
PowerApps: PowerApps;
|
|
176
|
-
PowerAutomate: PowerAutomate;
|
|
177
|
-
PowerPages: PowerPages;
|
|
178
|
-
ResourceQuery: ResourceQuery;
|
|
179
|
-
UserManagement: UserManagement;
|
|
180
|
-
WorkflowAgents: WorkflowAgents;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Global window interface extension for Power Platform API
|
|
186
|
-
*/
|
|
187
|
-
declare global {
|
|
188
|
-
interface Window {
|
|
189
|
-
/**
|
|
190
|
-
* Power Platform Admin API accessible to tools
|
|
191
|
-
* @example
|
|
192
|
-
* // Get admin app from Power Apps
|
|
193
|
-
* const app = await powerplatformAPI.PowerApps.Get('environments/{environmentId}/apps/{app}?api-version=2024-10-01');
|
|
194
|
-
*/
|
|
195
|
-
powerplatformAPI: PowerPlatformAPI.API;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export = PowerPlatformAPI;
|
|
200
|
-
export as namespace PowerPlatformAPI;
|