@pptb/types 1.2.1 → 1.2.2-beta.1
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 +114 -0
- package/bin/pptb-validate.js +74 -17
- package/index.d.ts +2 -0
- package/lib/validate.js +120 -1
- package/package.json +2 -1
- package/pptbConfig.d.ts +82 -0
- package/toolboxAPI.d.ts +78 -0
package/README.md
CHANGED
|
@@ -8,6 +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
|
+
- [pptb.config.json (optional)](#pptbconfigjson-optional)
|
|
11
12
|
- [Overview](#overview)
|
|
12
13
|
- [Usage](#usage)
|
|
13
14
|
- [Include all type definitions](#include-all-type-definitions)
|
|
@@ -17,6 +18,7 @@ TypeScript type definitions for Power Platform ToolBox APIs, plus a built-in CLI
|
|
|
17
18
|
- [Utilities](#utilities)
|
|
18
19
|
- [Terminal Operations](#terminal-operations)
|
|
19
20
|
- [Events](#events)
|
|
21
|
+
- [Inter-Tool Invocation](#inter-tool-invocation)
|
|
20
22
|
- [Dataverse API Examples](#dataverse-api-examples)
|
|
21
23
|
- [CRUD Operations](#crud-operations)
|
|
22
24
|
- [FetchXML Queries](#fetchxml-queries)
|
|
@@ -29,6 +31,7 @@ TypeScript type definitions for Power Platform ToolBox APIs, plus a built-in CLI
|
|
|
29
31
|
- [Utils](#utils)
|
|
30
32
|
- [Terminal](#terminal)
|
|
31
33
|
- [Events](#events-1)
|
|
34
|
+
- [Invocation](#invocation)
|
|
32
35
|
- [Dataverse API (`window.dataverseAPI`)](#dataverse-api-windowdataverseapi)
|
|
33
36
|
- [CRUD Operations](#crud-operations-1)
|
|
34
37
|
- [Query Operations](#query-operations)
|
|
@@ -109,6 +112,43 @@ The validator checks every field that the official review pipeline inspects:
|
|
|
109
112
|
|
|
110
113
|
> \* Required only when the `features` object is present.
|
|
111
114
|
|
|
115
|
+
#### pptb.config.json (optional)
|
|
116
|
+
|
|
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.
|
|
118
|
+
|
|
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.prefill` | ❌ | JSON-schema-style object describing data callers can pre-populate |
|
|
123
|
+
| `invocation.prefill.properties` | ❌ | Map of property names to `{ type?, enum?, items? }` descriptors |
|
|
124
|
+
| `invocation.returnTopic` | ❌ | JSON-schema-style object describing the data this tool returns to its caller |
|
|
125
|
+
| `invocation.returnTopic.properties` | ❌ | Map of property names to `{ type?, enum?, items? }` descriptors |
|
|
126
|
+
|
|
127
|
+
> \*\* Required only when the `invocation` object is present.
|
|
128
|
+
|
|
129
|
+
**Example `pptb.config.json`:**
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"invocation": {
|
|
134
|
+
"version": "1.0.0",
|
|
135
|
+
"prefill": {
|
|
136
|
+
"properties": {
|
|
137
|
+
"entityName": { "type": "string" },
|
|
138
|
+
"attributes": { "type": "array", "items": { "type": "string" } }
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"returnTopic": {
|
|
142
|
+
"properties": {
|
|
143
|
+
"result": { "type": "object" },
|
|
144
|
+
"status": { "type": "string", "enum": ["success", "cancelled", "error"] },
|
|
145
|
+
"error": { "type": "string" }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
112
152
|
## Overview
|
|
113
153
|
|
|
114
154
|
The `@pptb/types` package provides TypeScript definitions for two main APIs:
|
|
@@ -248,6 +288,67 @@ toolboxAPI.events.on((event, payload) => {
|
|
|
248
288
|
const history = await toolboxAPI.events.getHistory(10); // Last 10 events
|
|
249
289
|
```
|
|
250
290
|
|
|
291
|
+
### Inter-Tool Invocation
|
|
292
|
+
|
|
293
|
+
Tools can launch one another and pass data between them using the `invocation` namespace.
|
|
294
|
+
|
|
295
|
+
#### Caller: launching another tool with prefill data
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
// Tool A – launches the entity-picker tool and waits for a selection
|
|
299
|
+
const result = await toolboxAPI.invocation.launchTool(
|
|
300
|
+
"@my-org/entity-picker",
|
|
301
|
+
{ entityName: "account", allowMultiSelect: false },
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
if (result) {
|
|
305
|
+
console.log("Selected record id:", (result as { selectedId: string }).selectedId);
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### Callee: reading prefill data and returning a result
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// Tool B (@my-org/entity-picker) – reads the context provided by Tool A
|
|
313
|
+
const ctx = await toolboxAPI.invocation.getLaunchContext();
|
|
314
|
+
if (ctx) {
|
|
315
|
+
const entityName = ctx.entityName as string; // "account"
|
|
316
|
+
// … show records from entityName …
|
|
317
|
+
|
|
318
|
+
// When the user makes their selection:
|
|
319
|
+
await toolboxAPI.invocation.returnData({ selectedId: "a1b2c3...", selectedName: "Contoso" });
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
> **Tip:** A tool that was *not* launched by another tool receives `null` from `getLaunchContext()`.
|
|
324
|
+
> Use this to show a standalone UI or redirect accordingly.
|
|
325
|
+
|
|
326
|
+
#### Declaring your invocation contract
|
|
327
|
+
|
|
328
|
+
Add a `pptb.config.json` alongside your `package.json` to tell callers what data you expect and return:
|
|
329
|
+
|
|
330
|
+
```json
|
|
331
|
+
{
|
|
332
|
+
"invocation": {
|
|
333
|
+
"version": "1.0.0",
|
|
334
|
+
"prefill": {
|
|
335
|
+
"properties": {
|
|
336
|
+
"entityName": { "type": "string" },
|
|
337
|
+
"allowMultiSelect": { "type": "boolean" }
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
"returnTopic": {
|
|
341
|
+
"properties": {
|
|
342
|
+
"selectedId": { "type": "string" },
|
|
343
|
+
"selectedName": { "type": "string" }
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Run `pptb-validate` to validate both `package.json` and `pptb.config.json` at once.
|
|
351
|
+
|
|
251
352
|
## Dataverse API Examples
|
|
252
353
|
|
|
253
354
|
The Dataverse API provides direct access to Microsoft Dataverse operations:
|
|
@@ -495,6 +596,19 @@ Core platform features organized into namespaces:
|
|
|
495
596
|
- **off(callback: (event: any, payload: ToolBoxEventPayload) => void)**: void
|
|
496
597
|
- Removes a previously registered event listener
|
|
497
598
|
|
|
599
|
+
#### Invocation
|
|
600
|
+
|
|
601
|
+
- **getLaunchContext()**: Promise\<Record\<string, unknown\> | null\>
|
|
602
|
+
- Returns the prefill data passed by the tool that launched this tool, or `null` when not launched via inter-tool invocation
|
|
603
|
+
|
|
604
|
+
- **returnData(returnData: Record\<string, unknown\>)**: Promise\<void\>
|
|
605
|
+
- Sends data back to the caller tool and signals completion; no-op if not launched by another tool
|
|
606
|
+
|
|
607
|
+
- **launchTool(targetToolId, prefillData?, options?)**: Promise\<unknown\>
|
|
608
|
+
- Launches the specified tool, optionally with prefill data
|
|
609
|
+
- Returns a Promise that resolves with the data returned by the callee (or `null` if it closes without returning)
|
|
610
|
+
- `options.primaryConnectionId` / `options.secondaryConnectionId` – override connection for the callee
|
|
611
|
+
|
|
498
612
|
### Dataverse API (`window.dataverseAPI`)
|
|
499
613
|
|
|
500
614
|
Complete HTTP client for interacting with Microsoft Dataverse:
|
package/bin/pptb-validate.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
const fs = require("fs");
|
|
18
18
|
const path = require("path");
|
|
19
|
-
const { validatePackageJson } = require("../lib/validate");
|
|
19
|
+
const { validatePackageJson, validatePPTBConfig } = require("../lib/validate");
|
|
20
20
|
|
|
21
21
|
// ANSI colour helpers – gracefully degrade when colours are unsupported
|
|
22
22
|
const NO_COLOR = !process.stdout.isTTY || process.env.NO_COLOR;
|
|
@@ -37,7 +37,8 @@ ${c.bold("USAGE")}
|
|
|
37
37
|
pptb-validate [options] [path/to/package.json]
|
|
38
38
|
|
|
39
39
|
When no path is given the tool looks for ${c.cyan("package.json")} in the current
|
|
40
|
-
working directory.
|
|
40
|
+
working directory. If a ${c.cyan("pptb.config.json")} file exists in the same directory
|
|
41
|
+
it is automatically validated as well.
|
|
41
42
|
|
|
42
43
|
${c.bold("OPTIONS")}
|
|
43
44
|
${c.cyan("--skip-url-checks")} Skip URL reachability checks (faster, works offline)
|
|
@@ -79,6 +80,10 @@ async function main() {
|
|
|
79
80
|
packageJsonPath = path.resolve(process.cwd(), packageJsonPath);
|
|
80
81
|
}
|
|
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
|
+
|
|
82
87
|
// --- Load package.json ---
|
|
83
88
|
if (!fs.existsSync(packageJsonPath)) {
|
|
84
89
|
if (jsonOutput) {
|
|
@@ -102,21 +107,35 @@ async function main() {
|
|
|
102
107
|
process.exit(1);
|
|
103
108
|
}
|
|
104
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
|
+
|
|
105
121
|
// --- Run validation ---
|
|
106
122
|
if (!jsonOutput) {
|
|
107
123
|
console.log();
|
|
108
124
|
console.log(c.bold("Power Platform ToolBox – Tool Validator"));
|
|
109
125
|
console.log(c.dim("─".repeat(45)));
|
|
110
126
|
console.log(c.dim(`File: ${packageJsonPath}`));
|
|
127
|
+
if (pptbConfig !== null) {
|
|
128
|
+
console.log(c.dim(`Config: ${pptbConfigPath}`));
|
|
129
|
+
}
|
|
111
130
|
if (skipUrlChecks) {
|
|
112
131
|
console.log(c.yellow("⚠ URL reachability checks are skipped"));
|
|
113
132
|
}
|
|
114
133
|
console.log();
|
|
115
134
|
}
|
|
116
135
|
|
|
117
|
-
let
|
|
136
|
+
let packageResult;
|
|
118
137
|
try {
|
|
119
|
-
|
|
138
|
+
packageResult = await validatePackageJson(packageJson, { skipUrlChecks });
|
|
120
139
|
} catch (err) {
|
|
121
140
|
const message = err instanceof Error ? err.message : String(err);
|
|
122
141
|
if (jsonOutput) {
|
|
@@ -127,27 +146,62 @@ async function main() {
|
|
|
127
146
|
process.exit(1);
|
|
128
147
|
}
|
|
129
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
|
+
|
|
130
162
|
// --- Output results ---
|
|
131
163
|
if (jsonOutput) {
|
|
132
|
-
|
|
133
|
-
|
|
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);
|
|
134
173
|
}
|
|
135
174
|
|
|
136
|
-
// Human-readable output
|
|
137
|
-
if (
|
|
138
|
-
console.log(c.bold(c.red(`Errors (${
|
|
139
|
-
|
|
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}`));
|
|
140
179
|
console.log();
|
|
141
180
|
}
|
|
142
181
|
|
|
143
|
-
if (
|
|
144
|
-
console.log(c.bold(c.yellow(`Warnings (${
|
|
145
|
-
|
|
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}`));
|
|
146
185
|
console.log();
|
|
147
186
|
}
|
|
148
187
|
|
|
149
|
-
|
|
150
|
-
|
|
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;
|
|
151
205
|
console.log(c.green(c.bold("✔ Validation passed")));
|
|
152
206
|
console.log();
|
|
153
207
|
console.log(c.bold("Package summary"));
|
|
@@ -157,13 +211,16 @@ async function main() {
|
|
|
157
211
|
console.log(` Display name: ${info.displayName}`);
|
|
158
212
|
console.log(` Description : ${info.description}`);
|
|
159
213
|
console.log(` License : ${info.license}`);
|
|
160
|
-
console.log(` Contributors: ${info.contributors.map((
|
|
214
|
+
console.log(` Contributors: ${info.contributors.map((contributor) => contributor.name).join(", ")}`);
|
|
161
215
|
if (info.icon) {
|
|
162
216
|
console.log(` Icon : ${info.icon}`);
|
|
163
217
|
}
|
|
164
218
|
if (info.features) {
|
|
165
219
|
console.log(` Features : multiConnection=${info.features.multiConnection}${info.features.minAPI ? `, minAPI=${info.features.minAPI}` : ""}`);
|
|
166
220
|
}
|
|
221
|
+
if (configResult !== null && configResult.packageInfo && configResult.packageInfo.invocation) {
|
|
222
|
+
console.log(` Invocation : version=${configResult.packageInfo.invocation.version}`);
|
|
223
|
+
}
|
|
167
224
|
console.log();
|
|
168
225
|
} else {
|
|
169
226
|
console.log(c.red(c.bold("✖ Validation failed")));
|
|
@@ -172,7 +229,7 @@ async function main() {
|
|
|
172
229
|
console.log();
|
|
173
230
|
}
|
|
174
231
|
|
|
175
|
-
process.exit(
|
|
232
|
+
process.exit(overallValid ? 0 : 1);
|
|
176
233
|
}
|
|
177
234
|
|
|
178
235
|
main().catch((err) => {
|
package/index.d.ts
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
/// <reference path="./toolboxAPI.d.ts" />
|
|
18
18
|
/// <reference path="./dataverseAPI.d.ts" />
|
|
19
|
+
/// <reference path="./pptbConfig.d.ts" />
|
|
19
20
|
|
|
20
21
|
// Re-export all namespaces for convenience
|
|
21
22
|
export * from "./dataverseAPI";
|
|
22
23
|
export * from "./toolboxAPI";
|
|
24
|
+
export * from "./pptbConfig";
|
package/lib/validate.js
CHANGED
|
@@ -33,6 +33,19 @@
|
|
|
33
33
|
* }} ValidationResult
|
|
34
34
|
*/
|
|
35
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
|
+
|
|
36
49
|
// List of approved open source licenses
|
|
37
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"];
|
|
38
51
|
|
|
@@ -321,4 +334,110 @@ async function validatePackageJson(packageJson, options = {}) {
|
|
|
321
334
|
};
|
|
322
335
|
}
|
|
323
336
|
|
|
324
|
-
module.exports = { validatePackageJson, isValidUrl, APPROVED_LICENSES };
|
|
337
|
+
module.exports = { validatePackageJson, validatePPTBConfig, isValidUrl, APPROVED_LICENSES };
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Validates a tool's pptb.config.json against the official review criteria.
|
|
341
|
+
*
|
|
342
|
+
* @param {PPTBConfig} config - The parsed pptb.config.json object.
|
|
343
|
+
* @returns {ValidationResult}
|
|
344
|
+
*/
|
|
345
|
+
function validatePPTBConfig(config) {
|
|
346
|
+
const errors = /** @type {string[]} */ ([]);
|
|
347
|
+
const warnings = /** @type {string[]} */ ([]);
|
|
348
|
+
|
|
349
|
+
if (config === null || typeof config !== "object" || Array.isArray(config)) {
|
|
350
|
+
errors.push("pptb.config.json must be a JSON object");
|
|
351
|
+
return { valid: false, errors, warnings };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const VALID_ROOT_KEYS = ["invocation"];
|
|
355
|
+
const unknownRootKeys = Object.keys(config).filter((k) => !VALID_ROOT_KEYS.includes(k));
|
|
356
|
+
if (unknownRootKeys.length > 0) {
|
|
357
|
+
warnings.push(`pptb.config.json contains unrecognised root keys: ${unknownRootKeys.join(", ")}`);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Invocation section
|
|
361
|
+
if (config.invocation !== undefined) {
|
|
362
|
+
const inv = config.invocation;
|
|
363
|
+
|
|
364
|
+
if (inv === null || typeof inv !== "object" || Array.isArray(inv)) {
|
|
365
|
+
errors.push("invocation must be a non-array object");
|
|
366
|
+
} else {
|
|
367
|
+
// invocation.version – required, must be a valid semver string
|
|
368
|
+
if (inv.version === undefined || inv.version === null) {
|
|
369
|
+
errors.push("invocation.version is required");
|
|
370
|
+
} else if (typeof inv.version !== "string") {
|
|
371
|
+
errors.push("invocation.version must be a string");
|
|
372
|
+
} else if (!SEMVER_REGEX.test(inv.version)) {
|
|
373
|
+
errors.push(`invocation.version "${inv.version}" is not a valid semantic version string (e.g. "1.0.0")`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// invocation.prefill – optional JSON-schema-like object
|
|
377
|
+
if (inv.prefill !== undefined) {
|
|
378
|
+
if (inv.prefill === null || typeof inv.prefill !== "object" || Array.isArray(inv.prefill)) {
|
|
379
|
+
errors.push("invocation.prefill must be a non-array object");
|
|
380
|
+
} else if (inv.prefill.properties !== undefined) {
|
|
381
|
+
validateJsonSchemaProperties("invocation.prefill", inv.prefill.properties, errors);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// invocation.returnTopic – optional JSON-schema-like object
|
|
386
|
+
if (inv.returnTopic !== undefined) {
|
|
387
|
+
if (inv.returnTopic === null || typeof inv.returnTopic !== "object" || Array.isArray(inv.returnTopic)) {
|
|
388
|
+
errors.push("invocation.returnTopic must be a non-array object");
|
|
389
|
+
} else if (inv.returnTopic.properties !== undefined) {
|
|
390
|
+
validateJsonSchemaProperties("invocation.returnTopic", inv.returnTopic.properties, errors);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const valid = errors.length === 0;
|
|
397
|
+
|
|
398
|
+
return {
|
|
399
|
+
valid,
|
|
400
|
+
errors,
|
|
401
|
+
warnings,
|
|
402
|
+
packageInfo: valid
|
|
403
|
+
? {
|
|
404
|
+
invocation: config.invocation,
|
|
405
|
+
}
|
|
406
|
+
: undefined,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Validates a JSON-schema-style `properties` map used inside invocation sections.
|
|
412
|
+
* Only performs basic structural validation; full JSON Schema validation is not required.
|
|
413
|
+
*
|
|
414
|
+
* @param {string} fieldName
|
|
415
|
+
* @param {unknown} properties
|
|
416
|
+
* @param {string[]} errors
|
|
417
|
+
*/
|
|
418
|
+
function validateJsonSchemaProperties(fieldName, properties, errors) {
|
|
419
|
+
if (properties === null || typeof properties !== "object" || Array.isArray(properties)) {
|
|
420
|
+
errors.push(`${fieldName}.properties must be a non-array object`);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const propsRecord = /** @type {Record<string, unknown>} */ (properties);
|
|
425
|
+
|
|
426
|
+
for (const [key, value] of Object.entries(propsRecord)) {
|
|
427
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
428
|
+
errors.push(`${fieldName}.properties.${key} must be an object`);
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
const prop = /** @type {Record<string, unknown>} */ (value);
|
|
432
|
+
if (prop.type !== undefined && typeof prop.type !== "string") {
|
|
433
|
+
errors.push(`${fieldName}.properties.${key}.type must be a string`);
|
|
434
|
+
}
|
|
435
|
+
if (prop.enum !== undefined) {
|
|
436
|
+
if (!Array.isArray(prop.enum)) {
|
|
437
|
+
errors.push(`${fieldName}.properties.${key}.enum must be an array`);
|
|
438
|
+
} else if (prop.enum.length === 0) {
|
|
439
|
+
errors.push(`${fieldName}.properties.${key}.enum must not be empty`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pptb/types",
|
|
3
|
-
"version": "1.2.1",
|
|
3
|
+
"version": "1.2.2-beta.1",
|
|
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",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"index.d.ts",
|
|
27
27
|
"toolboxAPI.d.ts",
|
|
28
28
|
"dataverseAPI.d.ts",
|
|
29
|
+
"pptbConfig.d.ts",
|
|
29
30
|
"bin/",
|
|
30
31
|
"lib/",
|
|
31
32
|
"README.md"
|
package/pptbConfig.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for pptb.config.json – the optional configuration file that
|
|
3
|
+
* tools can place alongside their package.json to declare invocation contracts
|
|
4
|
+
* and other Power Platform ToolBox-specific metadata.
|
|
5
|
+
*
|
|
6
|
+
* Tool developers should place this file in the root of their tool package so
|
|
7
|
+
* that `pptb-validate` can automatically discover and validate it.
|
|
8
|
+
*
|
|
9
|
+
* Example pptb.config.json:
|
|
10
|
+
* ```json
|
|
11
|
+
* {
|
|
12
|
+
* "invocation": {
|
|
13
|
+
* "version": "1.0.0",
|
|
14
|
+
* "prefill": {
|
|
15
|
+
* "properties": {
|
|
16
|
+
* "entityName": { "type": "string" },
|
|
17
|
+
* "attributes": { "type": "array", "items": { "type": "string" } }
|
|
18
|
+
* }
|
|
19
|
+
* },
|
|
20
|
+
* "returnTopic": {
|
|
21
|
+
* "properties": {
|
|
22
|
+
* "result": { "type": "object" },
|
|
23
|
+
* "status": { "type": "string", "enum": ["success", "cancelled", "error"] },
|
|
24
|
+
* "error": { "type": "string" }
|
|
25
|
+
* }
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/** A JSON-schema-style property descriptor used inside invocation definitions. */
|
|
33
|
+
export interface JsonSchemaProperty {
|
|
34
|
+
/** The JSON type of the value (e.g. "string", "number", "boolean", "object", "array"). */
|
|
35
|
+
type?: string;
|
|
36
|
+
/** Restricts the value to one of the listed literals. */
|
|
37
|
+
enum?: string[];
|
|
38
|
+
/** Describes the items of an array property. */
|
|
39
|
+
items?: JsonSchemaProperty;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** A JSON-schema-style object definition: a map of named property descriptors. */
|
|
43
|
+
export interface JsonSchemaObject {
|
|
44
|
+
properties?: Record<string, JsonSchemaProperty>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The invocation contract for a tool.
|
|
49
|
+
*
|
|
50
|
+
* - `version` controls which revision of this contract is in effect. It must
|
|
51
|
+
* follow **semantic versioning** (`MAJOR.MINOR.PATCH[-prerelease][+build]`).
|
|
52
|
+
* Tool developers own this version and should bump it whenever the shape of
|
|
53
|
+
* `prefill` or `returnTopic` changes in a meaningful way.
|
|
54
|
+
* - `prefill` describes the data that callers can pre-populate when opening
|
|
55
|
+
* this tool programmatically.
|
|
56
|
+
* - `returnTopic` describes the data this tool will resolve back to its caller
|
|
57
|
+
* when it finishes.
|
|
58
|
+
*/
|
|
59
|
+
export interface InvocationConfig {
|
|
60
|
+
/**
|
|
61
|
+
* Semantic version of this invocation contract (e.g. "1.0.0").
|
|
62
|
+
* Tool developers control this version so they can evolve the prefill or
|
|
63
|
+
* return shape independently of the tool's npm package version.
|
|
64
|
+
*/
|
|
65
|
+
version: string;
|
|
66
|
+
/** Schema of the data the caller can pass in when invoking this tool. */
|
|
67
|
+
prefill?: JsonSchemaObject;
|
|
68
|
+
/** Schema of the data this tool returns to its caller on completion. */
|
|
69
|
+
returnTopic?: JsonSchemaObject;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The shape of a tool's `pptb.config.json` file.
|
|
74
|
+
*
|
|
75
|
+
* This file lives alongside `package.json` in the tool's package root.
|
|
76
|
+
* All sections are optional; the file itself is optional. When present it
|
|
77
|
+
* is validated by `pptb-validate` in addition to `package.json`.
|
|
78
|
+
*/
|
|
79
|
+
export interface PPTBConfig {
|
|
80
|
+
/** Invocation contract – how this tool can be called by other tools. */
|
|
81
|
+
invocation?: InvocationConfig;
|
|
82
|
+
}
|
package/toolboxAPI.d.ts
CHANGED
|
@@ -397,6 +397,14 @@ declare namespace ToolBoxAPI {
|
|
|
397
397
|
*/
|
|
398
398
|
events: EventsAPI;
|
|
399
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Inter-tool launch context API.
|
|
402
|
+
*
|
|
403
|
+
* Allows one tool to launch another, pass prefill data to it, and receive
|
|
404
|
+
* a return value when the callee finishes.
|
|
405
|
+
*/
|
|
406
|
+
invocation: InvocationAPI;
|
|
407
|
+
|
|
400
408
|
/**
|
|
401
409
|
* Get the current tool context
|
|
402
410
|
* @internal Used internally by the framework
|
|
@@ -404,6 +412,76 @@ declare namespace ToolBoxAPI {
|
|
|
404
412
|
getToolContext: () => Promise<ToolContext>;
|
|
405
413
|
}
|
|
406
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Inter-tool launch context API.
|
|
417
|
+
*
|
|
418
|
+
* Tools use this namespace to:
|
|
419
|
+
* 1. **Launch another tool** with prefill data (`invocation.launchTool`).
|
|
420
|
+
* 2. **Read their own launch context** when they were launched by another tool (`invocation.getLaunchContext`).
|
|
421
|
+
* 3. **Return data** back to the tool that launched them (`invocation.returnData`).
|
|
422
|
+
*
|
|
423
|
+
* @example Caller (Tool A)
|
|
424
|
+
* ```ts
|
|
425
|
+
* const result = await toolboxAPI.invocation.launchTool(
|
|
426
|
+
* "@my-org/entity-picker",
|
|
427
|
+
* { entityName: "account" },
|
|
428
|
+
* );
|
|
429
|
+
* console.log(result); // { selectedId: "...", selectedName: "..." }
|
|
430
|
+
* ```
|
|
431
|
+
*
|
|
432
|
+
* @example Callee (Tool B)
|
|
433
|
+
* ```ts
|
|
434
|
+
* const ctx = await toolboxAPI.invocation.getLaunchContext();
|
|
435
|
+
* if (ctx) {
|
|
436
|
+
* console.log(ctx.entityName); // "account"
|
|
437
|
+
* }
|
|
438
|
+
*
|
|
439
|
+
* // After user picks something...
|
|
440
|
+
* await toolboxAPI.invocation.returnData({ selectedId, selectedName });
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
export interface InvocationAPI {
|
|
444
|
+
/**
|
|
445
|
+
* Returns the prefill data that was passed by the caller tool when it launched
|
|
446
|
+
* this tool, or `null` if this tool was not launched via an inter-tool invocation.
|
|
447
|
+
*/
|
|
448
|
+
getLaunchContext: () => Promise<Record<string, unknown> | null>;
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Returns data back to the caller tool that launched this tool.
|
|
452
|
+
*
|
|
453
|
+
* The value resolves the `Promise` returned by the caller's
|
|
454
|
+
* `invocation.launchTool()` call. After calling `returnData`, the PPTB host
|
|
455
|
+
* will notify the caller; it is the callee's responsibility to close itself (or
|
|
456
|
+
* update its UI) after the return.
|
|
457
|
+
*
|
|
458
|
+
* If this tool was not launched by another tool, the call is a no-op.
|
|
459
|
+
*
|
|
460
|
+
* @param returnData The data to pass back to the caller
|
|
461
|
+
*/
|
|
462
|
+
returnData: (returnData: Record<string, unknown>) => Promise<void>;
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Launch another tool from within this tool and (optionally) pass prefill data.
|
|
466
|
+
*
|
|
467
|
+
* Returns a Promise that resolves with the data the target tool sends via
|
|
468
|
+
* `invocation.returnData()`, or `null` if the target tool closes without
|
|
469
|
+
* returning any data.
|
|
470
|
+
*
|
|
471
|
+
* The target tool must be installed and its `pptb.config.json` must declare an
|
|
472
|
+
* `invocation.prefill` schema that matches the shape of `prefillData`.
|
|
473
|
+
*
|
|
474
|
+
* @param targetToolId The npm package name (toolId) of the tool to launch
|
|
475
|
+
* @param prefillData Data to pre-populate the target tool's state
|
|
476
|
+
* @param options Optional connection overrides for the target tool
|
|
477
|
+
*/
|
|
478
|
+
launchTool: (
|
|
479
|
+
targetToolId: string,
|
|
480
|
+
prefillData?: Record<string, unknown>,
|
|
481
|
+
options?: { primaryConnectionId?: string | null; secondaryConnectionId?: string | null },
|
|
482
|
+
) => Promise<unknown>;
|
|
483
|
+
}
|
|
484
|
+
|
|
407
485
|
/**
|
|
408
486
|
* Auto-update event handlers
|
|
409
487
|
*/
|