pptb-standard-sample-tool 1.7.3 → 1.7.4
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 +137 -0
- package/dist/app.js +27 -18
- package/dist/features/dotnetWorker.js +142 -0
- package/dist/features/workerRpcAdapter.js +194 -0
- package/dist/index.html +13 -0
- package/npm-shrinkwrap.json +492 -7
- package/package.json +11 -4
- package/pptb.config.json +20 -1
package/README.md
CHANGED
|
@@ -56,6 +56,7 @@ This compiles the TypeScript source in `src/` to JavaScript in `dist/`.
|
|
|
56
56
|
|
|
57
57
|
```
|
|
58
58
|
html-sample/
|
|
59
|
+
├── dotnet/ # C# worker tool and shared Core project
|
|
59
60
|
├── src/
|
|
60
61
|
│ ├── app.ts # Main application logic (TypeScript)
|
|
61
62
|
│ ├── features/ # UI feature modules (terminal, filesystem, etc.)
|
|
@@ -170,6 +171,142 @@ const dataverse: typeof window.dataverseAPI = window.dataverseAPI;
|
|
|
170
171
|
3. **Rebuild:** Run `npm run build`
|
|
171
172
|
4. **Reload Tool:** In Power Platform Tool Box, close and reopen the tool
|
|
172
173
|
|
|
174
|
+
## .NET Worker Test
|
|
175
|
+
|
|
176
|
+
The **.NET Worker Test (Account Summary)** section is beside Query Records. The
|
|
177
|
+
worker summarizes up to ten accounts by country. The TypeScript callback builds
|
|
178
|
+
the FetchXML and calls Dataverse; the C# worker groups the returned rows and
|
|
179
|
+
returns `{ totalAccounts, countries: [{ country, count }] }`. The direct account
|
|
180
|
+
query remains independent and continues to use saved `demo.fetchxml`. Start,
|
|
181
|
+
summarize, cancel and stop are explicit; worker status/output is separate.
|
|
182
|
+
Loading the tool does not start .NET. The headless entry point is unchanged and
|
|
183
|
+
never starts a worker.
|
|
184
|
+
|
|
185
|
+
The complete C# sample is in `dotnet/Worker`; it has no separate Core project.
|
|
186
|
+
Workers run as the current OS user and **are not sandboxed**. No tokens, secrets
|
|
187
|
+
or credential environment variables are sent by this tool.
|
|
188
|
+
|
|
189
|
+
Tool code uses PPTB's worker session API rather than implementing a JSON-RPC
|
|
190
|
+
transport:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const worker = await toolboxAPI.workers.connect("sample", {
|
|
194
|
+
requests: {
|
|
195
|
+
"dataverse/getAccounts": (params, context) => {
|
|
196
|
+
const top = typeof params === "object" && params !== null && "top" in params ? params.top : undefined;
|
|
197
|
+
if (typeof top !== "number" || !Number.isSafeInteger(top) || top < 1 || top > 100) throw new Error("Expected an account limit from 1 to 100");
|
|
198
|
+
if (context.isCancellationRequested) throw new Error("Cancelled");
|
|
199
|
+
const fetchXml = `<fetch top="${top}"><entity name="account"><attribute name="name" /><attribute name="address1_country" /></entity></fetch>`;
|
|
200
|
+
return dataverseAPI.fetchXmlQuery(fetchXml).then((result) => ({ value: result.value }));
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
notifications: {
|
|
204
|
+
progress: (message) => showStatus(String(message)),
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await worker.ready;
|
|
209
|
+
const result = await worker.request("accounts/summarizeByCountry", { top: 10 });
|
|
210
|
+
await worker.stop();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
PPTB owns JSON-RPC framing, readiness buffering, request correlation,
|
|
214
|
+
cancellation, and transport disposal. Tool authors provide only the
|
|
215
|
+
worker-initiated callbacks they need and call named worker methods.
|
|
216
|
+
|
|
217
|
+
### Build And Pack
|
|
218
|
+
|
|
219
|
+
Use the PR7 desktop development build, Node.js, and a compatible installed .NET
|
|
220
|
+
10 SDK/runtime. This sample pins `@pptb/types@1.2.7-beta.6`, the current npm
|
|
221
|
+
beta tag, for `workers.connect()`. The shrinkwrap locks that exact release;
|
|
222
|
+
dependency changes use npm and the existing shrinkwrap workflow (`npm run finalize-package`).
|
|
223
|
+
The beta still depends on `@pptb/validate@1.0.4`, which warns that `workers` is
|
|
224
|
+
unrecognized. That validator result is not worker-declaration validation; the
|
|
225
|
+
current desktop worker validator remains authoritative.
|
|
226
|
+
|
|
227
|
+
From this `html-sample` directory:
|
|
228
|
+
|
|
229
|
+
```sh
|
|
230
|
+
npm install
|
|
231
|
+
npm test
|
|
232
|
+
npm run pack:worker
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
The pack command writes `PPTB.Sample.Query.Worker.0.1.4.nupkg` to `dotnet/feed/`.
|
|
236
|
+
The exact declaration is `PPTB.Sample.Query.Worker@0.1.4`, command
|
|
237
|
+
`pptb-sample-query-worker`, targeting `net10.0`. Inspect the `.nupkg`: it must
|
|
238
|
+
contain `Worker.dll`, managed dependencies, runtime/dependency manifests and
|
|
239
|
+
`DotnetToolSettings.xml`. Changing package bytes requires a fresh
|
|
240
|
+
worker version and a matching `packageVersion` update in `pptb.config.json`. Do
|
|
241
|
+
not overwrite a version with new bytes. `platforms: ["all"]` is the PPTB support
|
|
242
|
+
matrix, not qualification evidence.
|
|
243
|
+
|
|
244
|
+
### Load Local Tool
|
|
245
|
+
|
|
246
|
+
From the desktop-app repo in PowerShell, point to this repo's generated feed in
|
|
247
|
+
the main-process environment:
|
|
248
|
+
|
|
249
|
+
```powershell
|
|
250
|
+
$env:PPTB_DOTNET_LOCAL_NUGET_FEED = (Resolve-Path "../sample-tools/new/html-sample/dotnet/feed").Path
|
|
251
|
+
pnpm run dev
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
On macOS/Linux shells:
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
PPTB_DOTNET_LOCAL_NUGET_FEED="$(cd ../sample-tools/new/html-sample/dotnet/feed && pwd -P)" pnpm run dev
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
First run `npm run pack:worker` in `html-sample`. The feed must already exist
|
|
261
|
+
and be a flat directory containing only regular files, with no symlink path
|
|
262
|
+
components, subdirectories, symlink files or hard-linked files. On other OSes,
|
|
263
|
+
choose an absolute canonical directory satisfying the same rules. Set the
|
|
264
|
+
environment variable before launching PPTB; restart the development app after
|
|
265
|
+
changing it. The previously packed package is accessible at the canonical path
|
|
266
|
+
without moving or repacking it.
|
|
267
|
+
|
|
268
|
+
The feed requires both the Vite development bundle's main-only
|
|
269
|
+
`PPTB_DEVELOPER_BUILD=1` marker and an unpackaged app. Production-mode unpackaged
|
|
270
|
+
and packaged builds reject it. Never add the feed to the tool manifest or
|
|
271
|
+
renderer. Registry, marketplace and npm-debug installs remain nuget.org-only;
|
|
272
|
+
only the exact local worker package ID is mapped to this feed, and dependencies
|
|
273
|
+
remain on nuget.org. Source path/package digest changes require fresh consent.
|
|
274
|
+
|
|
275
|
+
1. Use **Load Local Tool** to load `sample-tools/new/html-sample` and select an
|
|
276
|
+
authenticated primary Dataverse connection.
|
|
277
|
+
2. Start worker, review native-code consent, and wait for Worker ready.
|
|
278
|
+
3. Select **Summarize Accounts by Country (Top 10)**. TypeScript sends
|
|
279
|
+
`accounts/summarizeByCountry({ top: 10 })`; C# calls the registered
|
|
280
|
+
`dataverse/getAccounts({ top: 10 })` callback. TypeScript builds the bounded
|
|
281
|
+
FetchXML and returns only account names and country values; C# groups those
|
|
282
|
+
rows and returns `{ totalAccounts, countries: [{ country, count }] }`.
|
|
283
|
+
4. Cancel a pending query, then stop explicitly. Cancellation is cooperative;
|
|
284
|
+
the existing Dataverse API does not abort an in-flight HTTP request. No request
|
|
285
|
+
is automatically replayed. A failed stop disables start/query until cleanup
|
|
286
|
+
is verified; Stop worker remains available to retry.
|
|
287
|
+
5. Also check consent denial/revocation, tool close and app quit.
|
|
288
|
+
|
|
289
|
+
### Verification Boundaries
|
|
290
|
+
|
|
291
|
+
`npm test` builds the browser bundle and runs simulated worker transport tests;
|
|
292
|
+
it does not launch C#. There were no pre-existing automated sample test scripts;
|
|
293
|
+
the interactive security suites remain available in the UI.
|
|
294
|
+
|
|
295
|
+
For an opt-in **real packaged C# stdio** test, extract the `.nupkg` into a temporary
|
|
296
|
+
directory, then run from `new/html-sample` with absolute paths:
|
|
297
|
+
|
|
298
|
+
```sh
|
|
299
|
+
node tests/dotnetWorker.smoke.mjs /usr/local/share/dotnet/dotnet \
|
|
300
|
+
/absolute/path/to/extracted/tools/net10.0/any/Worker.dll
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
This launches native code explicitly with a minimal credential-free environment.
|
|
304
|
+
It checks initialize, account summary, reverse callbacks with fixture data, progress,
|
|
305
|
+
cancellation and EOF exit. It does **not** test live Dataverse, local-feed restore,
|
|
306
|
+
PPTB consent/broker, or other OS/architecture targets. The full Load Local Tool
|
|
307
|
+
smoke remains a separate manual gate; do not infer PR7 completion from either
|
|
308
|
+
simulated transport tests or this real-stdio fixture.
|
|
309
|
+
|
|
173
310
|
## Security Testing Guidance
|
|
174
311
|
|
|
175
312
|
Use the built-in **Run Security Probe** button in the Terminal section to validate terminal exposure with safe commands only.
|
package/dist/app.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference types="@pptb/types" />
|
|
2
|
+
import { createDotnetWorkerFeature } from "./features/dotnetWorker.js";
|
|
2
3
|
import { createFileSystemFeature } from "./features/filesystem.js";
|
|
3
4
|
import { createTerminalFeature } from "./features/terminal.js";
|
|
4
5
|
import { createSecuritySuites } from "./security/suites.js";
|
|
@@ -27,6 +28,7 @@ let preventCloseActive = false;
|
|
|
27
28
|
let securitySuites = null;
|
|
28
29
|
let terminalFeature = null;
|
|
29
30
|
let fileSystemFeature = null;
|
|
31
|
+
let dotnetWorkerFeature = null;
|
|
30
32
|
let inputEntityName = null;
|
|
31
33
|
let fetchXmlFromLaunchContext = null;
|
|
32
34
|
const powerPlatformNamespaces = [
|
|
@@ -71,6 +73,12 @@ async function initialize() {
|
|
|
71
73
|
log,
|
|
72
74
|
getCurrentConnection: () => currentConnection,
|
|
73
75
|
});
|
|
76
|
+
dotnetWorkerFeature = createDotnetWorkerFeature({
|
|
77
|
+
toolbox,
|
|
78
|
+
dataverse,
|
|
79
|
+
getCurrentConnection: () => currentConnection,
|
|
80
|
+
log,
|
|
81
|
+
});
|
|
74
82
|
// Initialize security suites (used by UI buttons)
|
|
75
83
|
securitySuites = createSecuritySuites({
|
|
76
84
|
toolbox,
|
|
@@ -310,6 +318,10 @@ function setupEventHandlers() {
|
|
|
310
318
|
document.getElementById("query-accounts-secondary-btn")?.addEventListener("click", queryAccountsSecondary);
|
|
311
319
|
document.getElementById("query-accounts-third-btn")?.addEventListener("click", queryAccountsThird);
|
|
312
320
|
document.getElementById("query-contacts-querydata-btn")?.addEventListener("click", queryContactQueryData);
|
|
321
|
+
document.getElementById("worker-start-btn")?.addEventListener("click", () => dotnetWorkerFeature?.start());
|
|
322
|
+
document.getElementById("worker-query-btn")?.addEventListener("click", () => dotnetWorkerFeature?.query());
|
|
323
|
+
document.getElementById("worker-cancel-btn")?.addEventListener("click", () => dotnetWorkerFeature?.cancel());
|
|
324
|
+
document.getElementById("worker-stop-btn")?.addEventListener("click", () => dotnetWorkerFeature?.stop());
|
|
313
325
|
// CRUD buttons
|
|
314
326
|
document.getElementById("create-contact-btn")?.addEventListener("click", createContact);
|
|
315
327
|
document.getElementById("update-contact-btn")?.addEventListener("click", updateContact);
|
|
@@ -547,9 +559,20 @@ function handleTerminalOutput(data) {
|
|
|
547
559
|
function handleCommandCompleted(data) {
|
|
548
560
|
requireTerminalFeature().handleCommandCompleted(data);
|
|
549
561
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
562
|
+
async function getAccountFetchXml() {
|
|
563
|
+
const saved = await getSetting("demo.fetchxml");
|
|
564
|
+
return saved && saved.trim().length > 0
|
|
565
|
+
? saved
|
|
566
|
+
: `<fetch top="10">
|
|
567
|
+
<entity name="account">
|
|
568
|
+
<attribute name="name" />
|
|
569
|
+
<attribute name="accountid" />
|
|
570
|
+
<attribute name="emailaddress1" />
|
|
571
|
+
<attribute name="telephone1" />
|
|
572
|
+
<order attribute="name" />
|
|
573
|
+
</entity>
|
|
574
|
+
</fetch>`;
|
|
575
|
+
}
|
|
553
576
|
async function queryAccounts() {
|
|
554
577
|
if (!currentConnection) {
|
|
555
578
|
await showNotification("No Connection", "Please connect to a Dataverse environment", "warning");
|
|
@@ -559,21 +582,7 @@ async function queryAccounts() {
|
|
|
559
582
|
const output = document.getElementById("query-output");
|
|
560
583
|
if (output)
|
|
561
584
|
output.textContent = "Querying accounts...\n";
|
|
562
|
-
|
|
563
|
-
const saved = await getSetting("demo.fetchxml");
|
|
564
|
-
const fetchXml = saved && saved.trim().length > 0
|
|
565
|
-
? saved
|
|
566
|
-
: `
|
|
567
|
-
<fetch top="10">
|
|
568
|
-
<entity name="account">
|
|
569
|
-
<attribute name="name" />
|
|
570
|
-
<attribute name="accountid" />
|
|
571
|
-
<attribute name="emailaddress1" />
|
|
572
|
-
<attribute name="telephone1" />
|
|
573
|
-
<order attribute="name" />
|
|
574
|
-
</entity>
|
|
575
|
-
</fetch>
|
|
576
|
-
`.trim();
|
|
585
|
+
const fetchXml = await getAccountFetchXml();
|
|
577
586
|
const result = await dataverse.fetchXmlQuery(fetchXml);
|
|
578
587
|
if (output) {
|
|
579
588
|
output.textContent = `Found ${result.value.length} account(s):\n\n`;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// src/features/dotnetWorker.ts
|
|
2
|
+
function createDotnetWorkerFeature(deps) {
|
|
3
|
+
const startButton = document.getElementById("worker-start-btn");
|
|
4
|
+
const runButton = document.getElementById("worker-query-btn");
|
|
5
|
+
const cancelButton = document.getElementById("worker-cancel-btn");
|
|
6
|
+
const stopButton = document.getElementById("worker-stop-btn");
|
|
7
|
+
const status = document.getElementById("worker-status");
|
|
8
|
+
const output = document.getElementById("worker-output");
|
|
9
|
+
let worker;
|
|
10
|
+
let queryRunning = false;
|
|
11
|
+
let cancelRequested = false;
|
|
12
|
+
let state = "stopped";
|
|
13
|
+
function render() {
|
|
14
|
+
startButton.disabled = state !== "stopped" || !deps.toolbox.workers;
|
|
15
|
+
runButton.disabled = state !== "ready" || queryRunning;
|
|
16
|
+
cancelButton.disabled = state !== "ready" || !queryRunning;
|
|
17
|
+
stopButton.disabled = !worker || state === "stopping";
|
|
18
|
+
}
|
|
19
|
+
function report(error, prefix) {
|
|
20
|
+
const message = `${prefix}: ${error instanceof Error ? error.message : String(error)}`;
|
|
21
|
+
status.textContent = message;
|
|
22
|
+
deps.log(message, "error");
|
|
23
|
+
}
|
|
24
|
+
function clearWorker() {
|
|
25
|
+
worker?.dispose();
|
|
26
|
+
worker = void 0;
|
|
27
|
+
state = "stopped";
|
|
28
|
+
render();
|
|
29
|
+
}
|
|
30
|
+
async function start() {
|
|
31
|
+
if (state !== "stopped") return;
|
|
32
|
+
state = "starting";
|
|
33
|
+
render();
|
|
34
|
+
status.textContent = "Requesting native-code consent and starting worker...";
|
|
35
|
+
output.textContent = "";
|
|
36
|
+
try {
|
|
37
|
+
const started = await deps.toolbox.workers.connect("sample", {
|
|
38
|
+
requests: {
|
|
39
|
+
"dataverse/getAccounts": async (params, context) => {
|
|
40
|
+
const top = typeof params === "object" && params !== null && "top" in params ? params.top : void 0;
|
|
41
|
+
if (typeof top !== "number" || !Number.isSafeInteger(top) || top < 1 || top > 100) throw new Error("Expected an account limit from 1 to 100");
|
|
42
|
+
if (!deps.getCurrentConnection()) throw new Error("No active Dataverse connection");
|
|
43
|
+
if (context.isCancellationRequested) throw new Error("Request cancelled");
|
|
44
|
+
const fetchXml = `<fetch top="${top}"><entity name="account"><attribute name="name" /><attribute name="address1_country" /></entity></fetch>`;
|
|
45
|
+
const result = await deps.dataverse.fetchXmlQuery(fetchXml);
|
|
46
|
+
if (context.isCancellationRequested) throw new Error("Request cancelled");
|
|
47
|
+
return { value: result.value };
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
notifications: {
|
|
51
|
+
progress: (message) => {
|
|
52
|
+
if (state === "ready" && typeof message === "string") status.textContent = message;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
onExit: (snapshot) => {
|
|
56
|
+
clearWorker();
|
|
57
|
+
status.textContent = snapshot.failure ? `Worker exited: ${snapshot.failure}` : "Worker exited";
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
worker = started;
|
|
61
|
+
void started.ready.catch(() => void 0);
|
|
62
|
+
await started.ready;
|
|
63
|
+
if (worker !== started || state !== "starting") return;
|
|
64
|
+
state = "ready";
|
|
65
|
+
status.textContent = "Worker ready";
|
|
66
|
+
render();
|
|
67
|
+
} catch (error) {
|
|
68
|
+
if (worker) {
|
|
69
|
+
try {
|
|
70
|
+
await worker.stop();
|
|
71
|
+
clearWorker();
|
|
72
|
+
} catch (stopError) {
|
|
73
|
+
state = "blocked";
|
|
74
|
+
render();
|
|
75
|
+
report(stopError, "Startup failed; cleanup unverified. Retry Stop worker");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
clearWorker();
|
|
80
|
+
report(error, "Worker startup failed");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function query() {
|
|
84
|
+
if (state !== "ready" || !worker || queryRunning) return;
|
|
85
|
+
const activeWorker = worker;
|
|
86
|
+
queryRunning = true;
|
|
87
|
+
cancelRequested = false;
|
|
88
|
+
render();
|
|
89
|
+
output.textContent = "";
|
|
90
|
+
status.textContent = "Preparing account summary in worker...";
|
|
91
|
+
try {
|
|
92
|
+
if (!deps.getCurrentConnection()) throw new Error("No active Dataverse connection");
|
|
93
|
+
if (worker !== activeWorker || state !== "ready") return;
|
|
94
|
+
const result = await activeWorker.request("accounts/summarizeByCountry", { top: 10 });
|
|
95
|
+
if (worker !== activeWorker || state !== "ready") return;
|
|
96
|
+
if (cancelRequested) status.textContent = "Query cancelled";
|
|
97
|
+
else {
|
|
98
|
+
output.textContent = JSON.stringify(result, null, 2);
|
|
99
|
+
status.textContent = "Worker query completed";
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
if (worker === activeWorker && state === "ready") {
|
|
103
|
+
if (cancelRequested) status.textContent = "Query cancelled";
|
|
104
|
+
else report(error, "Worker query failed");
|
|
105
|
+
}
|
|
106
|
+
} finally {
|
|
107
|
+
queryRunning = false;
|
|
108
|
+
render();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function cancel() {
|
|
112
|
+
if (!worker || state !== "ready" || !queryRunning) return;
|
|
113
|
+
cancelRequested = true;
|
|
114
|
+
worker.cancel();
|
|
115
|
+
status.textContent = "Cancellation requested; an in-flight Dataverse call may still finish";
|
|
116
|
+
cancelButton.disabled = true;
|
|
117
|
+
}
|
|
118
|
+
async function stop() {
|
|
119
|
+
if (!worker || state === "stopping") return;
|
|
120
|
+
const stoppingWorker = worker;
|
|
121
|
+
state = "stopping";
|
|
122
|
+
stoppingWorker.cancel();
|
|
123
|
+
render();
|
|
124
|
+
status.textContent = "Stopping worker...";
|
|
125
|
+
try {
|
|
126
|
+
await stoppingWorker.stop();
|
|
127
|
+
if (worker === stoppingWorker) clearWorker();
|
|
128
|
+
status.textContent = "Worker stopped";
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (worker !== stoppingWorker) return;
|
|
131
|
+
state = "blocked";
|
|
132
|
+
render();
|
|
133
|
+
report(error, "Worker cleanup unverified. Retry Stop worker");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
render();
|
|
137
|
+
if (!deps.toolbox.workers) status.textContent = "Worker API unavailable in this PPTB build";
|
|
138
|
+
return { start, query, cancel, stop };
|
|
139
|
+
}
|
|
140
|
+
export {
|
|
141
|
+
createDotnetWorkerFeature
|
|
142
|
+
};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { AbstractMessageReader, AbstractMessageWriter, CancellationTokenSource, createMessageConnection, Disposable, ResponseError } from "vscode-jsonrpc/browser";
|
|
2
|
+
class WorkerMessageReader extends AbstractMessageReader {
|
|
3
|
+
maxQueuedMessages;
|
|
4
|
+
maxQueuedBytes;
|
|
5
|
+
callback;
|
|
6
|
+
queued = [];
|
|
7
|
+
queuedBytes = 0;
|
|
8
|
+
failed = false;
|
|
9
|
+
failure;
|
|
10
|
+
released = false;
|
|
11
|
+
constructor(maxQueuedMessages, maxQueuedBytes) {
|
|
12
|
+
super();
|
|
13
|
+
this.maxQueuedMessages = maxQueuedMessages;
|
|
14
|
+
this.maxQueuedBytes = maxQueuedBytes;
|
|
15
|
+
}
|
|
16
|
+
listen(callback) {
|
|
17
|
+
if (this.callback)
|
|
18
|
+
throw new Error("Worker RPC reader is already listening");
|
|
19
|
+
this.callback = callback;
|
|
20
|
+
return Disposable.create(() => {
|
|
21
|
+
this.callback = undefined;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
flush() {
|
|
25
|
+
if (!this.callback)
|
|
26
|
+
return;
|
|
27
|
+
this.released = true;
|
|
28
|
+
for (const message of this.queued.splice(0))
|
|
29
|
+
this.callback(message);
|
|
30
|
+
this.queuedBytes = 0;
|
|
31
|
+
if (this.failure) {
|
|
32
|
+
this.fireError(this.failure);
|
|
33
|
+
this.fireClose();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
accept(message) {
|
|
37
|
+
if (this.callback && this.released) {
|
|
38
|
+
this.callback(message);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (this.failed)
|
|
42
|
+
return;
|
|
43
|
+
let bytes;
|
|
44
|
+
try {
|
|
45
|
+
bytes = new TextEncoder().encode(JSON.stringify(message)).byteLength;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
this.failed = true;
|
|
49
|
+
this.failure = error instanceof Error ? error : new Error(String(error));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (this.queued.length >= this.maxQueuedMessages || this.queuedBytes + bytes > this.maxQueuedBytes) {
|
|
53
|
+
this.failed = true;
|
|
54
|
+
this.queued.length = 0;
|
|
55
|
+
this.queuedBytes = 0;
|
|
56
|
+
this.failure = new Error("Worker RPC startup message buffer limit exceeded");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.queued.push(message);
|
|
60
|
+
this.queuedBytes += bytes;
|
|
61
|
+
}
|
|
62
|
+
close() {
|
|
63
|
+
this.fireClose();
|
|
64
|
+
}
|
|
65
|
+
dispose() {
|
|
66
|
+
this.queued.length = 0;
|
|
67
|
+
this.queuedBytes = 0;
|
|
68
|
+
super.dispose();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
class WorkerMessageWriter extends AbstractMessageWriter {
|
|
72
|
+
transport;
|
|
73
|
+
constructor(transport) {
|
|
74
|
+
super();
|
|
75
|
+
this.transport = transport;
|
|
76
|
+
}
|
|
77
|
+
async write(message) {
|
|
78
|
+
try {
|
|
79
|
+
await this.transport.send(message);
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
this.fireError(error, message);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
end() {
|
|
87
|
+
this.fireClose();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export function createWorkerRpcClient(transport) {
|
|
91
|
+
const reader = new WorkerMessageReader(64, 4 * 1024 * 1024);
|
|
92
|
+
const writer = new WorkerMessageWriter(transport);
|
|
93
|
+
const connection = createMessageConnection(reader, writer);
|
|
94
|
+
connection.listen();
|
|
95
|
+
let disposed = false;
|
|
96
|
+
let unsubscribeMessage = () => undefined;
|
|
97
|
+
let unsubscribeExit = () => undefined;
|
|
98
|
+
const close = () => {
|
|
99
|
+
if (disposed)
|
|
100
|
+
return;
|
|
101
|
+
disposed = true;
|
|
102
|
+
unsubscribeMessage();
|
|
103
|
+
unsubscribeExit();
|
|
104
|
+
reader.close();
|
|
105
|
+
writer.end();
|
|
106
|
+
connection.dispose();
|
|
107
|
+
};
|
|
108
|
+
unsubscribeMessage = transport.onMessage((message) => {
|
|
109
|
+
if (!disposed)
|
|
110
|
+
reader.accept(message);
|
|
111
|
+
});
|
|
112
|
+
unsubscribeExit = transport.onExit(close);
|
|
113
|
+
let listening = false;
|
|
114
|
+
return {
|
|
115
|
+
connection,
|
|
116
|
+
listen: () => {
|
|
117
|
+
if (disposed)
|
|
118
|
+
throw new Error("Worker RPC client is disposed");
|
|
119
|
+
if (listening)
|
|
120
|
+
return;
|
|
121
|
+
listening = true;
|
|
122
|
+
reader.flush();
|
|
123
|
+
},
|
|
124
|
+
dispose: close,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export async function startWorkerSession(options) {
|
|
128
|
+
const workers = options.toolbox.workers;
|
|
129
|
+
if (!workers)
|
|
130
|
+
throw new Error("This PPTB build does not expose the worker API");
|
|
131
|
+
const started = await workers.start(options.workerId);
|
|
132
|
+
const rpc = createWorkerRpcClient({
|
|
133
|
+
send: (message) => workers.send(started.handle, message),
|
|
134
|
+
onMessage: (listener) => workers.onMessage(started.handle, listener),
|
|
135
|
+
onExit: (listener) => workers.onExit(started.handle, listener),
|
|
136
|
+
});
|
|
137
|
+
let currentQuery;
|
|
138
|
+
let disposed = false;
|
|
139
|
+
const dispose = () => {
|
|
140
|
+
if (disposed)
|
|
141
|
+
return;
|
|
142
|
+
disposed = true;
|
|
143
|
+
currentQuery?.cancel();
|
|
144
|
+
currentQuery?.dispose();
|
|
145
|
+
currentQuery = undefined;
|
|
146
|
+
unsubscribeExit();
|
|
147
|
+
rpc.dispose();
|
|
148
|
+
};
|
|
149
|
+
rpc.connection.onRequest("dataverse/fetchXml", async (fetchXml, token) => {
|
|
150
|
+
if (typeof fetchXml !== "string" || !fetchXml.trim())
|
|
151
|
+
throw new ResponseError(-32602, "Expected FetchXML text");
|
|
152
|
+
const result = await options.fetchXmlQuery(fetchXml, token);
|
|
153
|
+
if (token.isCancellationRequested)
|
|
154
|
+
throw new ResponseError(-32800, "Request cancelled");
|
|
155
|
+
return JSON.parse(JSON.stringify({ value: result.value }));
|
|
156
|
+
});
|
|
157
|
+
rpc.connection.onNotification("progress", (message) => {
|
|
158
|
+
if (typeof message === "string")
|
|
159
|
+
options.onProgress?.(message);
|
|
160
|
+
});
|
|
161
|
+
const unsubscribeExit = workers.onExit(started.handle, (snapshot) => {
|
|
162
|
+
dispose();
|
|
163
|
+
options.onExit?.(snapshot);
|
|
164
|
+
});
|
|
165
|
+
rpc.listen();
|
|
166
|
+
return {
|
|
167
|
+
ready: started.ready,
|
|
168
|
+
query: async (fetchXml) => {
|
|
169
|
+
if (disposed)
|
|
170
|
+
throw new Error("Worker session has ended");
|
|
171
|
+
if (currentQuery)
|
|
172
|
+
throw new Error("A worker query is already running");
|
|
173
|
+
const query = new CancellationTokenSource();
|
|
174
|
+
currentQuery = query;
|
|
175
|
+
try {
|
|
176
|
+
return await rpc.connection.sendRequest("query", fetchXml, query.token);
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
query.dispose();
|
|
180
|
+
if (currentQuery === query)
|
|
181
|
+
currentQuery = undefined;
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
cancel: () => currentQuery?.cancel(),
|
|
185
|
+
stop: async () => {
|
|
186
|
+
if (disposed)
|
|
187
|
+
return;
|
|
188
|
+
currentQuery?.cancel();
|
|
189
|
+
await workers.stop(started.handle);
|
|
190
|
+
dispose();
|
|
191
|
+
},
|
|
192
|
+
dispose,
|
|
193
|
+
};
|
|
194
|
+
}
|
package/dist/index.html
CHANGED
|
@@ -161,6 +161,19 @@
|
|
|
161
161
|
<div id="query-output-third" class="output"></div>
|
|
162
162
|
</div>
|
|
163
163
|
|
|
164
|
+
<div class="example-group">
|
|
165
|
+
<h3>.NET Worker Test (Account Summary)</h3>
|
|
166
|
+
<p class="info-box warning">Native code runs as your OS user and is not sandboxed. The worker groups account rows by country.</p>
|
|
167
|
+
<div class="button-group">
|
|
168
|
+
<button id="worker-start-btn" class="btn">Start worker</button>
|
|
169
|
+
<button id="worker-query-btn" class="btn btn-primary" disabled>Summarize Accounts by Country (Top 10)</button>
|
|
170
|
+
<button id="worker-cancel-btn" class="btn btn-warning" disabled>Cancel summary</button>
|
|
171
|
+
<button id="worker-stop-btn" class="btn btn-error" disabled>Stop worker</button>
|
|
172
|
+
</div>
|
|
173
|
+
<div id="worker-status" role="status" aria-live="polite">Worker stopped</div>
|
|
174
|
+
<div id="worker-output" class="output"></div>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
164
177
|
<div class="example-group">
|
|
165
178
|
<h3>CRUD Operations</h3>
|
|
166
179
|
<div class="input-group">
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pptb-standard-sample-tool",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pptb-standard-sample-tool",
|
|
9
|
-
"version": "1.7.
|
|
9
|
+
"version": "1.7.3",
|
|
10
10
|
"license": "GPL-3.0",
|
|
11
11
|
"devDependencies": {
|
|
12
|
-
"@pptb/types": "
|
|
12
|
+
"@pptb/types": "1.2.7-beta.6",
|
|
13
|
+
"esbuild": "0.25.12",
|
|
13
14
|
"shx": "^0.4.0",
|
|
14
15
|
"typescript": "^5.0.0"
|
|
15
16
|
},
|
|
@@ -17,6 +18,448 @@
|
|
|
17
18
|
"node": ">=18.0.0"
|
|
18
19
|
}
|
|
19
20
|
},
|
|
21
|
+
"node_modules/@esbuild/aix-ppc64": {
|
|
22
|
+
"version": "0.25.12",
|
|
23
|
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
|
|
24
|
+
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
|
|
25
|
+
"cpu": [
|
|
26
|
+
"ppc64"
|
|
27
|
+
],
|
|
28
|
+
"dev": true,
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"optional": true,
|
|
31
|
+
"os": [
|
|
32
|
+
"aix"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"node_modules/@esbuild/android-arm": {
|
|
39
|
+
"version": "0.25.12",
|
|
40
|
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
|
|
41
|
+
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
|
|
42
|
+
"cpu": [
|
|
43
|
+
"arm"
|
|
44
|
+
],
|
|
45
|
+
"dev": true,
|
|
46
|
+
"license": "MIT",
|
|
47
|
+
"optional": true,
|
|
48
|
+
"os": [
|
|
49
|
+
"android"
|
|
50
|
+
],
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"node_modules/@esbuild/android-arm64": {
|
|
56
|
+
"version": "0.25.12",
|
|
57
|
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
|
|
58
|
+
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
|
|
59
|
+
"cpu": [
|
|
60
|
+
"arm64"
|
|
61
|
+
],
|
|
62
|
+
"dev": true,
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"optional": true,
|
|
65
|
+
"os": [
|
|
66
|
+
"android"
|
|
67
|
+
],
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"node_modules/@esbuild/android-x64": {
|
|
73
|
+
"version": "0.25.12",
|
|
74
|
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
|
|
75
|
+
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
|
|
76
|
+
"cpu": [
|
|
77
|
+
"x64"
|
|
78
|
+
],
|
|
79
|
+
"dev": true,
|
|
80
|
+
"license": "MIT",
|
|
81
|
+
"optional": true,
|
|
82
|
+
"os": [
|
|
83
|
+
"android"
|
|
84
|
+
],
|
|
85
|
+
"engines": {
|
|
86
|
+
"node": ">=18"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"node_modules/@esbuild/darwin-arm64": {
|
|
90
|
+
"version": "0.25.12",
|
|
91
|
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
|
|
92
|
+
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
|
|
93
|
+
"cpu": [
|
|
94
|
+
"arm64"
|
|
95
|
+
],
|
|
96
|
+
"dev": true,
|
|
97
|
+
"license": "MIT",
|
|
98
|
+
"optional": true,
|
|
99
|
+
"os": [
|
|
100
|
+
"darwin"
|
|
101
|
+
],
|
|
102
|
+
"engines": {
|
|
103
|
+
"node": ">=18"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"node_modules/@esbuild/darwin-x64": {
|
|
107
|
+
"version": "0.25.12",
|
|
108
|
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
|
|
109
|
+
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
|
|
110
|
+
"cpu": [
|
|
111
|
+
"x64"
|
|
112
|
+
],
|
|
113
|
+
"dev": true,
|
|
114
|
+
"license": "MIT",
|
|
115
|
+
"optional": true,
|
|
116
|
+
"os": [
|
|
117
|
+
"darwin"
|
|
118
|
+
],
|
|
119
|
+
"engines": {
|
|
120
|
+
"node": ">=18"
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
"node_modules/@esbuild/freebsd-arm64": {
|
|
124
|
+
"version": "0.25.12",
|
|
125
|
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
|
|
126
|
+
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
|
|
127
|
+
"cpu": [
|
|
128
|
+
"arm64"
|
|
129
|
+
],
|
|
130
|
+
"dev": true,
|
|
131
|
+
"license": "MIT",
|
|
132
|
+
"optional": true,
|
|
133
|
+
"os": [
|
|
134
|
+
"freebsd"
|
|
135
|
+
],
|
|
136
|
+
"engines": {
|
|
137
|
+
"node": ">=18"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"node_modules/@esbuild/freebsd-x64": {
|
|
141
|
+
"version": "0.25.12",
|
|
142
|
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
|
|
143
|
+
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
|
|
144
|
+
"cpu": [
|
|
145
|
+
"x64"
|
|
146
|
+
],
|
|
147
|
+
"dev": true,
|
|
148
|
+
"license": "MIT",
|
|
149
|
+
"optional": true,
|
|
150
|
+
"os": [
|
|
151
|
+
"freebsd"
|
|
152
|
+
],
|
|
153
|
+
"engines": {
|
|
154
|
+
"node": ">=18"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"node_modules/@esbuild/linux-arm": {
|
|
158
|
+
"version": "0.25.12",
|
|
159
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
|
|
160
|
+
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
|
|
161
|
+
"cpu": [
|
|
162
|
+
"arm"
|
|
163
|
+
],
|
|
164
|
+
"dev": true,
|
|
165
|
+
"license": "MIT",
|
|
166
|
+
"optional": true,
|
|
167
|
+
"os": [
|
|
168
|
+
"linux"
|
|
169
|
+
],
|
|
170
|
+
"engines": {
|
|
171
|
+
"node": ">=18"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"node_modules/@esbuild/linux-arm64": {
|
|
175
|
+
"version": "0.25.12",
|
|
176
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
|
|
177
|
+
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
|
|
178
|
+
"cpu": [
|
|
179
|
+
"arm64"
|
|
180
|
+
],
|
|
181
|
+
"dev": true,
|
|
182
|
+
"license": "MIT",
|
|
183
|
+
"optional": true,
|
|
184
|
+
"os": [
|
|
185
|
+
"linux"
|
|
186
|
+
],
|
|
187
|
+
"engines": {
|
|
188
|
+
"node": ">=18"
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
"node_modules/@esbuild/linux-ia32": {
|
|
192
|
+
"version": "0.25.12",
|
|
193
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
|
|
194
|
+
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
|
|
195
|
+
"cpu": [
|
|
196
|
+
"ia32"
|
|
197
|
+
],
|
|
198
|
+
"dev": true,
|
|
199
|
+
"license": "MIT",
|
|
200
|
+
"optional": true,
|
|
201
|
+
"os": [
|
|
202
|
+
"linux"
|
|
203
|
+
],
|
|
204
|
+
"engines": {
|
|
205
|
+
"node": ">=18"
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"node_modules/@esbuild/linux-loong64": {
|
|
209
|
+
"version": "0.25.12",
|
|
210
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
|
|
211
|
+
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
|
|
212
|
+
"cpu": [
|
|
213
|
+
"loong64"
|
|
214
|
+
],
|
|
215
|
+
"dev": true,
|
|
216
|
+
"license": "MIT",
|
|
217
|
+
"optional": true,
|
|
218
|
+
"os": [
|
|
219
|
+
"linux"
|
|
220
|
+
],
|
|
221
|
+
"engines": {
|
|
222
|
+
"node": ">=18"
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
"node_modules/@esbuild/linux-mips64el": {
|
|
226
|
+
"version": "0.25.12",
|
|
227
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
|
|
228
|
+
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
|
|
229
|
+
"cpu": [
|
|
230
|
+
"mips64el"
|
|
231
|
+
],
|
|
232
|
+
"dev": true,
|
|
233
|
+
"license": "MIT",
|
|
234
|
+
"optional": true,
|
|
235
|
+
"os": [
|
|
236
|
+
"linux"
|
|
237
|
+
],
|
|
238
|
+
"engines": {
|
|
239
|
+
"node": ">=18"
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"node_modules/@esbuild/linux-ppc64": {
|
|
243
|
+
"version": "0.25.12",
|
|
244
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
|
|
245
|
+
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
|
|
246
|
+
"cpu": [
|
|
247
|
+
"ppc64"
|
|
248
|
+
],
|
|
249
|
+
"dev": true,
|
|
250
|
+
"license": "MIT",
|
|
251
|
+
"optional": true,
|
|
252
|
+
"os": [
|
|
253
|
+
"linux"
|
|
254
|
+
],
|
|
255
|
+
"engines": {
|
|
256
|
+
"node": ">=18"
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
"node_modules/@esbuild/linux-riscv64": {
|
|
260
|
+
"version": "0.25.12",
|
|
261
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
|
|
262
|
+
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
|
|
263
|
+
"cpu": [
|
|
264
|
+
"riscv64"
|
|
265
|
+
],
|
|
266
|
+
"dev": true,
|
|
267
|
+
"license": "MIT",
|
|
268
|
+
"optional": true,
|
|
269
|
+
"os": [
|
|
270
|
+
"linux"
|
|
271
|
+
],
|
|
272
|
+
"engines": {
|
|
273
|
+
"node": ">=18"
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
"node_modules/@esbuild/linux-s390x": {
|
|
277
|
+
"version": "0.25.12",
|
|
278
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
|
|
279
|
+
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
|
|
280
|
+
"cpu": [
|
|
281
|
+
"s390x"
|
|
282
|
+
],
|
|
283
|
+
"dev": true,
|
|
284
|
+
"license": "MIT",
|
|
285
|
+
"optional": true,
|
|
286
|
+
"os": [
|
|
287
|
+
"linux"
|
|
288
|
+
],
|
|
289
|
+
"engines": {
|
|
290
|
+
"node": ">=18"
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
"node_modules/@esbuild/linux-x64": {
|
|
294
|
+
"version": "0.25.12",
|
|
295
|
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
|
|
296
|
+
"integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
|
|
297
|
+
"cpu": [
|
|
298
|
+
"x64"
|
|
299
|
+
],
|
|
300
|
+
"dev": true,
|
|
301
|
+
"license": "MIT",
|
|
302
|
+
"optional": true,
|
|
303
|
+
"os": [
|
|
304
|
+
"linux"
|
|
305
|
+
],
|
|
306
|
+
"engines": {
|
|
307
|
+
"node": ">=18"
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
"node_modules/@esbuild/netbsd-arm64": {
|
|
311
|
+
"version": "0.25.12",
|
|
312
|
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
|
|
313
|
+
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
|
|
314
|
+
"cpu": [
|
|
315
|
+
"arm64"
|
|
316
|
+
],
|
|
317
|
+
"dev": true,
|
|
318
|
+
"license": "MIT",
|
|
319
|
+
"optional": true,
|
|
320
|
+
"os": [
|
|
321
|
+
"netbsd"
|
|
322
|
+
],
|
|
323
|
+
"engines": {
|
|
324
|
+
"node": ">=18"
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
"node_modules/@esbuild/netbsd-x64": {
|
|
328
|
+
"version": "0.25.12",
|
|
329
|
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
|
|
330
|
+
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
|
|
331
|
+
"cpu": [
|
|
332
|
+
"x64"
|
|
333
|
+
],
|
|
334
|
+
"dev": true,
|
|
335
|
+
"license": "MIT",
|
|
336
|
+
"optional": true,
|
|
337
|
+
"os": [
|
|
338
|
+
"netbsd"
|
|
339
|
+
],
|
|
340
|
+
"engines": {
|
|
341
|
+
"node": ">=18"
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
"node_modules/@esbuild/openbsd-arm64": {
|
|
345
|
+
"version": "0.25.12",
|
|
346
|
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
|
|
347
|
+
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
|
|
348
|
+
"cpu": [
|
|
349
|
+
"arm64"
|
|
350
|
+
],
|
|
351
|
+
"dev": true,
|
|
352
|
+
"license": "MIT",
|
|
353
|
+
"optional": true,
|
|
354
|
+
"os": [
|
|
355
|
+
"openbsd"
|
|
356
|
+
],
|
|
357
|
+
"engines": {
|
|
358
|
+
"node": ">=18"
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
"node_modules/@esbuild/openbsd-x64": {
|
|
362
|
+
"version": "0.25.12",
|
|
363
|
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
|
|
364
|
+
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
|
|
365
|
+
"cpu": [
|
|
366
|
+
"x64"
|
|
367
|
+
],
|
|
368
|
+
"dev": true,
|
|
369
|
+
"license": "MIT",
|
|
370
|
+
"optional": true,
|
|
371
|
+
"os": [
|
|
372
|
+
"openbsd"
|
|
373
|
+
],
|
|
374
|
+
"engines": {
|
|
375
|
+
"node": ">=18"
|
|
376
|
+
}
|
|
377
|
+
},
|
|
378
|
+
"node_modules/@esbuild/openharmony-arm64": {
|
|
379
|
+
"version": "0.25.12",
|
|
380
|
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
|
|
381
|
+
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
|
|
382
|
+
"cpu": [
|
|
383
|
+
"arm64"
|
|
384
|
+
],
|
|
385
|
+
"dev": true,
|
|
386
|
+
"license": "MIT",
|
|
387
|
+
"optional": true,
|
|
388
|
+
"os": [
|
|
389
|
+
"openharmony"
|
|
390
|
+
],
|
|
391
|
+
"engines": {
|
|
392
|
+
"node": ">=18"
|
|
393
|
+
}
|
|
394
|
+
},
|
|
395
|
+
"node_modules/@esbuild/sunos-x64": {
|
|
396
|
+
"version": "0.25.12",
|
|
397
|
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
|
|
398
|
+
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
|
|
399
|
+
"cpu": [
|
|
400
|
+
"x64"
|
|
401
|
+
],
|
|
402
|
+
"dev": true,
|
|
403
|
+
"license": "MIT",
|
|
404
|
+
"optional": true,
|
|
405
|
+
"os": [
|
|
406
|
+
"sunos"
|
|
407
|
+
],
|
|
408
|
+
"engines": {
|
|
409
|
+
"node": ">=18"
|
|
410
|
+
}
|
|
411
|
+
},
|
|
412
|
+
"node_modules/@esbuild/win32-arm64": {
|
|
413
|
+
"version": "0.25.12",
|
|
414
|
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
|
|
415
|
+
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
|
|
416
|
+
"cpu": [
|
|
417
|
+
"arm64"
|
|
418
|
+
],
|
|
419
|
+
"dev": true,
|
|
420
|
+
"license": "MIT",
|
|
421
|
+
"optional": true,
|
|
422
|
+
"os": [
|
|
423
|
+
"win32"
|
|
424
|
+
],
|
|
425
|
+
"engines": {
|
|
426
|
+
"node": ">=18"
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
"node_modules/@esbuild/win32-ia32": {
|
|
430
|
+
"version": "0.25.12",
|
|
431
|
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
|
|
432
|
+
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
|
|
433
|
+
"cpu": [
|
|
434
|
+
"ia32"
|
|
435
|
+
],
|
|
436
|
+
"dev": true,
|
|
437
|
+
"license": "MIT",
|
|
438
|
+
"optional": true,
|
|
439
|
+
"os": [
|
|
440
|
+
"win32"
|
|
441
|
+
],
|
|
442
|
+
"engines": {
|
|
443
|
+
"node": ">=18"
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
"node_modules/@esbuild/win32-x64": {
|
|
447
|
+
"version": "0.25.12",
|
|
448
|
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
|
|
449
|
+
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
|
|
450
|
+
"cpu": [
|
|
451
|
+
"x64"
|
|
452
|
+
],
|
|
453
|
+
"dev": true,
|
|
454
|
+
"license": "MIT",
|
|
455
|
+
"optional": true,
|
|
456
|
+
"os": [
|
|
457
|
+
"win32"
|
|
458
|
+
],
|
|
459
|
+
"engines": {
|
|
460
|
+
"node": ">=18"
|
|
461
|
+
}
|
|
462
|
+
},
|
|
20
463
|
"node_modules/@isaacs/fs-minipass": {
|
|
21
464
|
"version": "4.0.1",
|
|
22
465
|
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
|
@@ -69,9 +512,9 @@
|
|
|
69
512
|
}
|
|
70
513
|
},
|
|
71
514
|
"node_modules/@pptb/types": {
|
|
72
|
-
"version": "1.2.7-beta.
|
|
73
|
-
"resolved": "https://registry.npmjs.org/@pptb/types/-/types-1.2.7-beta.
|
|
74
|
-
"integrity": "sha512-
|
|
515
|
+
"version": "1.2.7-beta.6",
|
|
516
|
+
"resolved": "https://registry.npmjs.org/@pptb/types/-/types-1.2.7-beta.6.tgz",
|
|
517
|
+
"integrity": "sha512-qBmkS5NtDu9dfiftV2Jlnjcdoc8EVBNLCVgH39jVbeCJkvqXcfRfsMapGeIKV55pEo0IGtfq+6LZXMI09TP/1w==",
|
|
75
518
|
"dev": true,
|
|
76
519
|
"license": "GPL-3.0",
|
|
77
520
|
"dependencies": {
|
|
@@ -144,6 +587,48 @@
|
|
|
144
587
|
"once": "^1.4.0"
|
|
145
588
|
}
|
|
146
589
|
},
|
|
590
|
+
"node_modules/esbuild": {
|
|
591
|
+
"version": "0.25.12",
|
|
592
|
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
|
|
593
|
+
"integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
|
|
594
|
+
"dev": true,
|
|
595
|
+
"hasInstallScript": true,
|
|
596
|
+
"license": "MIT",
|
|
597
|
+
"bin": {
|
|
598
|
+
"esbuild": "bin/esbuild"
|
|
599
|
+
},
|
|
600
|
+
"engines": {
|
|
601
|
+
"node": ">=18"
|
|
602
|
+
},
|
|
603
|
+
"optionalDependencies": {
|
|
604
|
+
"@esbuild/aix-ppc64": "0.25.12",
|
|
605
|
+
"@esbuild/android-arm": "0.25.12",
|
|
606
|
+
"@esbuild/android-arm64": "0.25.12",
|
|
607
|
+
"@esbuild/android-x64": "0.25.12",
|
|
608
|
+
"@esbuild/darwin-arm64": "0.25.12",
|
|
609
|
+
"@esbuild/darwin-x64": "0.25.12",
|
|
610
|
+
"@esbuild/freebsd-arm64": "0.25.12",
|
|
611
|
+
"@esbuild/freebsd-x64": "0.25.12",
|
|
612
|
+
"@esbuild/linux-arm": "0.25.12",
|
|
613
|
+
"@esbuild/linux-arm64": "0.25.12",
|
|
614
|
+
"@esbuild/linux-ia32": "0.25.12",
|
|
615
|
+
"@esbuild/linux-loong64": "0.25.12",
|
|
616
|
+
"@esbuild/linux-mips64el": "0.25.12",
|
|
617
|
+
"@esbuild/linux-ppc64": "0.25.12",
|
|
618
|
+
"@esbuild/linux-riscv64": "0.25.12",
|
|
619
|
+
"@esbuild/linux-s390x": "0.25.12",
|
|
620
|
+
"@esbuild/linux-x64": "0.25.12",
|
|
621
|
+
"@esbuild/netbsd-arm64": "0.25.12",
|
|
622
|
+
"@esbuild/netbsd-x64": "0.25.12",
|
|
623
|
+
"@esbuild/openbsd-arm64": "0.25.12",
|
|
624
|
+
"@esbuild/openbsd-x64": "0.25.12",
|
|
625
|
+
"@esbuild/openharmony-arm64": "0.25.12",
|
|
626
|
+
"@esbuild/sunos-x64": "0.25.12",
|
|
627
|
+
"@esbuild/win32-arm64": "0.25.12",
|
|
628
|
+
"@esbuild/win32-ia32": "0.25.12",
|
|
629
|
+
"@esbuild/win32-x64": "0.25.12"
|
|
630
|
+
}
|
|
631
|
+
},
|
|
147
632
|
"node_modules/execa": {
|
|
148
633
|
"version": "1.0.0",
|
|
149
634
|
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
|
|
@@ -729,4 +1214,4 @@
|
|
|
729
1214
|
}
|
|
730
1215
|
}
|
|
731
1216
|
}
|
|
732
|
-
}
|
|
1217
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pptb-standard-sample-tool",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.4",
|
|
4
4
|
"displayName": "HTML Sample Tool",
|
|
5
5
|
"description": "A sample Power Platform ToolBox tool built with HTML, CSS, and TypeScript",
|
|
6
6
|
"main": "index.html",
|
|
@@ -73,7 +73,10 @@
|
|
|
73
73
|
"node": ">=18.0.0"
|
|
74
74
|
},
|
|
75
75
|
"scripts": {
|
|
76
|
-
"build": "tsc && npm run copy-html && npm run copy-css && npm run copy-icon",
|
|
76
|
+
"build": "tsc && npm run build-worker && npm run copy-html && npm run copy-css && npm run copy-icon",
|
|
77
|
+
"build-worker": "esbuild src/features/dotnetWorker.ts --bundle --format=esm --platform=browser --outfile=dist/features/dotnetWorker.js",
|
|
78
|
+
"pack:worker": "dotnet pack dotnet/Worker/Worker.csproj --configuration Release --output dotnet/feed -p:RestoreLockedMode=true",
|
|
79
|
+
"test": "npm run build && node --test tests/dotnetWorker.test.mjs",
|
|
77
80
|
"copy-html": "shx cp src/index.html dist/",
|
|
78
81
|
"copy-css": "shx cp src/styles.css dist/",
|
|
79
82
|
"copy-icon": "shx cp -r icon/ dist/icon/",
|
|
@@ -81,7 +84,8 @@
|
|
|
81
84
|
"validate": "pptb-validate"
|
|
82
85
|
},
|
|
83
86
|
"devDependencies": {
|
|
84
|
-
"@pptb/types": "
|
|
87
|
+
"@pptb/types": "1.2.7-beta.6",
|
|
88
|
+
"esbuild": "0.25.12",
|
|
85
89
|
"shx": "^0.4.0",
|
|
86
90
|
"typescript": "^5.0.0"
|
|
87
91
|
},
|
|
@@ -89,5 +93,8 @@
|
|
|
89
93
|
"dist",
|
|
90
94
|
"npm-shrinkwrap.json",
|
|
91
95
|
"pptb.config.json"
|
|
92
|
-
]
|
|
96
|
+
],
|
|
97
|
+
"allowScripts": {
|
|
98
|
+
"esbuild@0.25.12": true
|
|
99
|
+
}
|
|
93
100
|
}
|
package/pptb.config.json
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
{
|
|
2
|
+
"workers": {
|
|
3
|
+
"sample": {
|
|
4
|
+
"kind": "dotnet-tool",
|
|
5
|
+
"packageId": "PPTB.Sample.Query.Worker",
|
|
6
|
+
"packageVersion": "0.1.5",
|
|
7
|
+
"command": "pptb-sample-query-worker",
|
|
8
|
+
"dotnet": {
|
|
9
|
+
"targetFramework": "net10.0",
|
|
10
|
+
"minimumRuntimeVersion": "10.0.0",
|
|
11
|
+
"rollForward": "Major"
|
|
12
|
+
},
|
|
13
|
+
"platforms": [
|
|
14
|
+
"all"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
},
|
|
2
18
|
"invocation": {
|
|
3
19
|
"version": "1.0.0",
|
|
4
20
|
"capabilities": [
|
|
@@ -22,9 +38,12 @@
|
|
|
22
38
|
"fetchXml": {
|
|
23
39
|
"type": "string"
|
|
24
40
|
},
|
|
25
|
-
"
|
|
41
|
+
"totalAccounts": {
|
|
26
42
|
"type": "number"
|
|
27
43
|
},
|
|
44
|
+
"countries": {
|
|
45
|
+
"type": "array"
|
|
46
|
+
},
|
|
28
47
|
"records": {
|
|
29
48
|
"type": "array"
|
|
30
49
|
}
|