@aiot-nexus/plugin-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +74 -0
  2. package/dev-host/host.html +344 -0
  3. package/dev-host/main.js +694 -0
  4. package/dev-host/preload.js +22 -0
  5. package/dist/.generated/plugin-framework-version.d.ts +11 -0
  6. package/dist/.generated/plugin-framework-version.js +39 -0
  7. package/dist/.generated/plugin-manifest.schema.json +106 -0
  8. package/dist/browser/frame-client.d.ts +26 -0
  9. package/dist/browser/frame-client.mjs +239 -0
  10. package/dist/src/browser/frame-client.d.ts +26 -0
  11. package/dist/src/browser/frame-client.js +244 -0
  12. package/dist/src/cli/run.d.ts +7 -0
  13. package/dist/src/cli/run.js +270 -0
  14. package/dist/src/cli.d.ts +2 -0
  15. package/dist/src/cli.js +9 -0
  16. package/dist/src/dev.d.ts +36 -0
  17. package/dist/src/dev.js +224 -0
  18. package/dist/src/index.d.ts +7 -0
  19. package/dist/src/index.js +27 -0
  20. package/dist/src/scaffold.d.ts +23 -0
  21. package/dist/src/scaffold.js +306 -0
  22. package/dist/src/types.d.ts +142 -0
  23. package/dist/src/types.js +2 -0
  24. package/dist/src/validation.d.ts +9 -0
  25. package/dist/src/validation.js +160 -0
  26. package/package.json +47 -0
  27. package/templates/minimal/.gitignore.tmpl +4 -0
  28. package/templates/minimal/.vscode/launch.json +22 -0
  29. package/templates/minimal/README.md.tmpl +22 -0
  30. package/templates/minimal/scripts/build.js.tmpl +83 -0
  31. package/templates/minimal/src/main.ts.tmpl +23 -0
  32. package/templates/minimal/src/renderer/app.ts.tmpl +194 -0
  33. package/templates/minimal/src/renderer/index.html +34 -0
  34. package/templates/minimal/src/renderer/plugin.css +164 -0
  35. package/templates/minimal/tsconfig.main.json +15 -0
  36. package/templates/minimal/tsconfig.renderer.json +17 -0
  37. package/templates/sqlite/.gitignore.tmpl +4 -0
  38. package/templates/sqlite/.vscode/launch.json +22 -0
  39. package/templates/sqlite/README.md.tmpl +22 -0
  40. package/templates/sqlite/scripts/build.js.tmpl +143 -0
  41. package/templates/sqlite/src/better-sqlite3.d.ts +22 -0
  42. package/templates/sqlite/src/main.ts.tmpl +190 -0
  43. package/templates/sqlite/src/renderer/app.ts.tmpl +428 -0
  44. package/templates/sqlite/src/renderer/index.html +57 -0
  45. package/templates/sqlite/src/renderer/plugin.css +275 -0
  46. package/templates/sqlite/tsconfig.main.json +15 -0
  47. package/templates/sqlite/tsconfig.renderer.json +17 -0
@@ -0,0 +1,194 @@
1
+ import { createPluginFrameClient } from "@aiot-nexus/plugin-sdk/browser";
2
+
3
+ interface EchoResponse {
4
+ value: string;
5
+ echoedAt: string;
6
+ }
7
+
8
+ type SupportedLanguage = "en-US" | "zh-CN";
9
+
10
+ const englishMessages = {
11
+ "document.title": "Plugin Echo",
12
+ "heading.echo": "Echo",
13
+ "section.request": "Request",
14
+ "field.value": "Value",
15
+ "action.send": "Send",
16
+ "result.response": "Response",
17
+ "status.connecting": "Connecting",
18
+ "status.waiting": "Waiting",
19
+ "status.ready": "Ready",
20
+ "status.sending": "Sending",
21
+ "status.error": "Error",
22
+ "validation.enterValue": "Enter a value.",
23
+ "surface.business": "Business",
24
+ "surface.tool": "Tool",
25
+ "surface.settings": "Settings"
26
+ } as const;
27
+
28
+ type MessageKey = keyof typeof englishMessages;
29
+
30
+ const messages: Record<SupportedLanguage, Record<MessageKey, string>> = {
31
+ "en-US": englishMessages,
32
+ "zh-CN": {
33
+ "document.title": "插件回显",
34
+ "heading.echo": "回显",
35
+ "section.request": "请求",
36
+ "field.value": "内容",
37
+ "action.send": "发送",
38
+ "result.response": "响应",
39
+ "status.connecting": "正在连接",
40
+ "status.waiting": "等待中",
41
+ "status.ready": "已就绪",
42
+ "status.sending": "正在发送",
43
+ "status.error": "错误",
44
+ "validation.enterValue": "请输入内容。",
45
+ "surface.business": "业务",
46
+ "surface.tool": "工具",
47
+ "surface.settings": "设置"
48
+ }
49
+ };
50
+
51
+ type OutputState =
52
+ | { kind: "message"; key: MessageKey }
53
+ | { kind: "literal"; value: string };
54
+
55
+ const client = createPluginFrameClient();
56
+ const input = requireElement<HTMLInputElement>("echoInput");
57
+ const button = requireElement<HTMLButtonElement>("echoButton");
58
+ const resultOutput = requireElement<HTMLOutputElement>("echoResult");
59
+ const contextOutput = requireElement<HTMLElement>("pluginContext");
60
+ const connectionState = requireElement<HTMLElement>("connectionState");
61
+
62
+ let language: SupportedLanguage = "en-US";
63
+ let pluginId = "";
64
+ let surface = "";
65
+ let connectionStateKey: MessageKey = "status.waiting";
66
+ let outputState: OutputState = { kind: "message", key: "status.waiting" };
67
+
68
+ button.addEventListener("click", () => {
69
+ void echoValue();
70
+ });
71
+ input.addEventListener("keydown", (event) => {
72
+ if (event.key === "Enter") {
73
+ event.preventDefault();
74
+ void echoValue();
75
+ }
76
+ });
77
+ const stopContextUpdates = client.onContextChanged((context) => {
78
+ pluginId = context.pluginId;
79
+ surface = context.surface;
80
+ applyLanguage(resolveLanguage(context.language));
81
+ });
82
+ window.addEventListener("beforeunload", () => {
83
+ stopContextUpdates();
84
+ client.dispose();
85
+ });
86
+
87
+ applyLanguage(language);
88
+
89
+ void client.waitForContext().then((context) => {
90
+ pluginId = context.pluginId;
91
+ surface = context.surface;
92
+ connectionStateKey = "status.ready";
93
+ applyLanguage(resolveLanguage(context.language));
94
+ }).catch((error: unknown) => {
95
+ connectionStateKey = "status.error";
96
+ outputState = { kind: "literal", value: errorMessage(error) };
97
+ renderLocalizedState();
98
+ });
99
+
100
+ async function echoValue(): Promise<void> {
101
+ const value = input.value.trim();
102
+ if (!value) {
103
+ setOutputMessage("validation.enterValue");
104
+ return;
105
+ }
106
+ button.disabled = true;
107
+ setConnectionState("status.sending");
108
+ try {
109
+ const result = await client.invokeMain<Record<string, unknown>, EchoResponse>("demo.echo", { value });
110
+ if (!result.ok) {
111
+ setOutputLiteral(result.error.code + ": " + result.error.message);
112
+ return;
113
+ }
114
+ setOutputLiteral(result.value.value + " (" + result.value.echoedAt + ")");
115
+ } catch (error: unknown) {
116
+ setOutputLiteral(errorMessage(error));
117
+ } finally {
118
+ button.disabled = false;
119
+ setConnectionState("status.ready");
120
+ }
121
+ }
122
+
123
+ function applyLanguage(nextLanguage: SupportedLanguage): void {
124
+ language = nextLanguage;
125
+ document.documentElement.lang = language;
126
+ document.querySelectorAll<HTMLElement>("[data-i18n]").forEach((element) => {
127
+ const key = element.dataset.i18n;
128
+ if (key && isMessageKey(key)) {
129
+ element.textContent = text(key);
130
+ }
131
+ });
132
+ renderLocalizedState();
133
+ client.notifyResize();
134
+ }
135
+
136
+ function renderLocalizedState(): void {
137
+ contextOutput.textContent = pluginId
138
+ ? pluginId + " / " + localizedSurface(surface)
139
+ : text("status.connecting");
140
+ connectionState.textContent = text(connectionStateKey);
141
+ resultOutput.textContent = outputState.kind === "message" ? text(outputState.key) : outputState.value;
142
+ }
143
+
144
+ function setConnectionState(key: MessageKey): void {
145
+ connectionStateKey = key;
146
+ connectionState.textContent = text(key);
147
+ }
148
+
149
+ function setOutputMessage(key: MessageKey): void {
150
+ outputState = { kind: "message", key };
151
+ resultOutput.textContent = text(key);
152
+ }
153
+
154
+ function setOutputLiteral(value: string): void {
155
+ outputState = { kind: "literal", value };
156
+ resultOutput.textContent = value;
157
+ }
158
+
159
+ function localizedSurface(value: string): string {
160
+ if (value === "business") {
161
+ return text("surface.business");
162
+ }
163
+ if (value === "tool") {
164
+ return text("surface.tool");
165
+ }
166
+ if (value === "settings") {
167
+ return text("surface.settings");
168
+ }
169
+ return value;
170
+ }
171
+
172
+ function resolveLanguage(value: string): SupportedLanguage {
173
+ return value.toLowerCase().startsWith("zh") ? "zh-CN" : "en-US";
174
+ }
175
+
176
+ function text(key: MessageKey): string {
177
+ return messages[language][key];
178
+ }
179
+
180
+ function isMessageKey(value: string): value is MessageKey {
181
+ return Object.prototype.hasOwnProperty.call(englishMessages, value);
182
+ }
183
+
184
+ function requireElement<T extends HTMLElement>(id: string): T {
185
+ const element = document.getElementById(id);
186
+ if (!element) {
187
+ throw new Error("Required element is missing: " + id);
188
+ }
189
+ return element as T;
190
+ }
191
+
192
+ function errorMessage(error: unknown): string {
193
+ return error instanceof Error ? error.message : String(error);
194
+ }
@@ -0,0 +1,34 @@
1
+ <!doctype html>
2
+ <html lang="en-US">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title data-i18n="document.title">Plugin Echo</title>
7
+ <link rel="stylesheet" href="assets/plugin.css">
8
+ </head>
9
+ <body>
10
+ <main class="plugin-shell">
11
+ <header class="plugin-header">
12
+ <div>
13
+ <h1 data-i18n="heading.echo">Echo</h1>
14
+ <p id="pluginContext" data-i18n="status.connecting">Connecting</p>
15
+ </div>
16
+ <span id="connectionState" class="status" data-i18n="status.waiting">Waiting</span>
17
+ </header>
18
+
19
+ <section class="echo-panel" aria-labelledby="echoTitle">
20
+ <h2 id="echoTitle" data-i18n="section.request">Request</h2>
21
+ <div class="echo-controls">
22
+ <label for="echoInput" data-i18n="field.value">Value</label>
23
+ <input id="echoInput" type="text" maxlength="200" autocomplete="off" value="Hello Nexus">
24
+ <button id="echoButton" type="button" data-i18n="action.send">Send</button>
25
+ </div>
26
+ <div class="result-row">
27
+ <span data-i18n="result.response">Response</span>
28
+ <output id="echoResult" data-i18n="status.waiting">Waiting</output>
29
+ </div>
30
+ </section>
31
+ </main>
32
+ <script type="module" src="assets/app.js"></script>
33
+ </body>
34
+ </html>
@@ -0,0 +1,164 @@
1
+ :root {
2
+ color-scheme: light dark;
3
+ font-family: "Segoe UI", Arial, sans-serif;
4
+ background: #f3f5f7;
5
+ color: #20252b;
6
+ }
7
+
8
+ * {
9
+ box-sizing: border-box;
10
+ }
11
+
12
+ body {
13
+ margin: 0;
14
+ min-height: 0;
15
+ background: #f3f5f7;
16
+ }
17
+
18
+ .plugin-shell {
19
+ display: grid;
20
+ gap: 12px;
21
+ padding: 16px;
22
+ }
23
+
24
+ .plugin-header,
25
+ .echo-panel {
26
+ border: 1px solid #d4d9df;
27
+ border-radius: 8px;
28
+ background: #ffffff;
29
+ }
30
+
31
+ .plugin-header {
32
+ display: flex;
33
+ align-items: flex-start;
34
+ justify-content: space-between;
35
+ gap: 16px;
36
+ padding: 14px 16px;
37
+ }
38
+
39
+ h1,
40
+ h2,
41
+ p {
42
+ margin: 0;
43
+ }
44
+
45
+ h1 {
46
+ font-size: 22px;
47
+ }
48
+
49
+ h2 {
50
+ font-size: 15px;
51
+ }
52
+
53
+ p,
54
+ .result-row > span {
55
+ margin-top: 4px;
56
+ color: #65717e;
57
+ font-size: 12px;
58
+ }
59
+
60
+ .status {
61
+ color: #12663f;
62
+ font-size: 12px;
63
+ font-weight: 600;
64
+ }
65
+
66
+ .echo-panel {
67
+ padding: 16px;
68
+ }
69
+
70
+ .echo-controls {
71
+ display: grid;
72
+ grid-template-columns: auto minmax(160px, 1fr) auto;
73
+ gap: 10px;
74
+ align-items: center;
75
+ margin-top: 12px;
76
+ }
77
+
78
+ label {
79
+ font-size: 13px;
80
+ font-weight: 600;
81
+ }
82
+
83
+ input {
84
+ min-width: 0;
85
+ height: 36px;
86
+ border: 1px solid #aeb7c2;
87
+ border-radius: 6px;
88
+ padding: 0 10px;
89
+ background: #ffffff;
90
+ color: #20252b;
91
+ font: inherit;
92
+ }
93
+
94
+ button {
95
+ height: 36px;
96
+ border: 1px solid #1768a6;
97
+ border-radius: 6px;
98
+ padding: 0 16px;
99
+ background: #1768a6;
100
+ color: #ffffff;
101
+ font: inherit;
102
+ font-weight: 600;
103
+ cursor: pointer;
104
+ }
105
+
106
+ button:disabled {
107
+ cursor: default;
108
+ opacity: 0.55;
109
+ }
110
+
111
+ .result-row {
112
+ display: grid;
113
+ grid-template-columns: 80px minmax(0, 1fr);
114
+ gap: 10px;
115
+ margin-top: 14px;
116
+ border-top: 1px solid #e7eaee;
117
+ padding-top: 12px;
118
+ }
119
+
120
+ output {
121
+ overflow-wrap: anywhere;
122
+ font-family: Consolas, monospace;
123
+ font-size: 12px;
124
+ }
125
+
126
+ @media (max-width: 560px) {
127
+ .echo-controls,
128
+ .result-row {
129
+ grid-template-columns: 1fr;
130
+ }
131
+ }
132
+
133
+ @media (prefers-color-scheme: dark) {
134
+ :root,
135
+ body {
136
+ background: #16191d;
137
+ color: #edf0f2;
138
+ }
139
+
140
+ .plugin-header,
141
+ .echo-panel {
142
+ border-color: #454b52;
143
+ background: #24282d;
144
+ }
145
+
146
+ p,
147
+ .result-row > span {
148
+ color: #aeb7c2;
149
+ }
150
+
151
+ input {
152
+ border-color: #59616a;
153
+ background: #191c20;
154
+ color: #edf0f2;
155
+ }
156
+
157
+ .result-row {
158
+ border-top-color: #454b52;
159
+ }
160
+
161
+ .status {
162
+ color: #75d6a5;
163
+ }
164
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "moduleResolution": "node",
6
+ "lib": ["ES2020"],
7
+ "rootDir": "src",
8
+ "outDir": ".build/main",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "types": ["node"]
13
+ },
14
+ "include": ["src/main.ts"]
15
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ES2020",
5
+ "moduleResolution": "node",
6
+ "lib": ["ES2020", "DOM"],
7
+ "baseUrl": ".",
8
+ "paths": {
9
+ "@aiot-nexus/plugin-sdk/browser": ["node_modules/@aiot-nexus/plugin-sdk/dist/browser/frame-client.d.ts"]
10
+ },
11
+ "rootDir": "src/renderer",
12
+ "outDir": ".build/renderer",
13
+ "strict": true,
14
+ "skipLibCheck": true
15
+ },
16
+ "include": ["src/renderer/app.ts"]
17
+ }
@@ -0,0 +1,4 @@
1
+ node_modules/
2
+ dist/
3
+ .build/
4
+ .nexus-plugin-dev/
@@ -0,0 +1,22 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Debug Nexus plugin",
6
+ "type": "node-terminal",
7
+ "request": "launch",
8
+ "command": "npm run dev:debug",
9
+ "cwd": "${workspaceFolder}"
10
+ },
11
+ {
12
+ "name": "Attach Nexus plugin main (9229)",
13
+ "type": "node",
14
+ "request": "attach",
15
+ "address": "127.0.0.1",
16
+ "port": 9229,
17
+ "sourceMaps": true,
18
+ "outFiles": ["${workspaceFolder}/dist/**/*.cjs"],
19
+ "skipFiles": ["<node_internals>/**"]
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,22 @@
1
+ # __DISPLAY_NAME_MARKDOWN__
2
+
3
+ AIoT Nexus independent PluginFrame plugin generated from the `sqlite` template.
4
+
5
+ ```bash
6
+ npm install
7
+ npm run typecheck
8
+ npm run build
9
+ npm run dev
10
+ ```
11
+
12
+ Every `npm run build` rebuilds `better-sqlite3@8.7.0` for Electron 22.3.27 with `@electron/rebuild@3.7.2` before packaging. `npm run rebuild:native` remains available as a standalone diagnostic command.
13
+
14
+ The deployable plugin package is `dist/__PLUGIN_ID__/`. Copy that directory to the Nexus executable's `Plugins/__PLUGIN_ID__/` directory, then restart Nexus. Runtime data is stored in the host-provided plugin data directory and is not written into the plugin package.
15
+
16
+ The renderer follows `context.language` from Nexus, subscribes to in-place context updates without reloading the iframe, and includes `en-US` and `zh-CN` message catalogs. Static labels, CRUD status messages, action buttons, validation text, and date formatting switch together. Edit renderer messages in `src/renderer/app.ts`; edit host-shell titles and descriptions in `src/manifest.json` resources.
17
+
18
+ When this project was created from a local SDK archive, `vendor/aiot-nexus-plugin-sdk.tgz` is part of the source project and must be kept when the project is moved or committed.
19
+
20
+ For main-process breakpoints, open this plugin directory in VS Code, set breakpoints in `src/main.ts`, and run `Debug Nexus plugin`. If child-process auto attach is disabled, start `npm run dev:debug` and select `Attach Nexus plugin main (9229)`. Renderer DevTools opens automatically, with `src/renderer/app.ts` available through the development source map. The default debug build also runs the Electron 22.3.27 native rebuild before launch. Use `npm run dev -- --no-build` only when intentionally reusing a compatible current `dist` output.
21
+
22
+ The independent host stores the SQLite database and settings under `.nexus-plugin-dev/data/` and mirrors the PluginFrame protocol and current Host API surface. Run the deployable `dist/__PLUGIN_ID__/` in Nexus before delivery because the standalone host does not reproduce the full application shell or routing state.
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const fs = require("fs");
5
+ const path = require("path");
6
+ const { spawnSync } = require("child_process");
7
+ const ts = require("typescript");
8
+
9
+ const pluginId = "__PLUGIN_ID__";
10
+ const pluginRoot = path.resolve(__dirname, "..");
11
+ const buildDirectory = path.join(pluginRoot, ".build");
12
+ const outputDirectory = path.join(pluginRoot, "dist", pluginId);
13
+ const nodeModulesDirectory = path.join(pluginRoot, "node_modules");
14
+ const sdkModuleSpecifier = "@aiot-nexus/plugin-sdk/browser";
15
+ const runtimePackages = ["better-sqlite3", "bindings", "file-uri-to-path"];
16
+
17
+ run();
18
+
19
+ function run() {
20
+ verifyNativeDependency();
21
+ fs.rmSync(buildDirectory, { recursive: true, force: true });
22
+ fs.rmSync(outputDirectory, { recursive: true, force: true });
23
+ runTypeScript("tsconfig.main.json");
24
+ runTypeScript("tsconfig.renderer.json");
25
+
26
+ fs.mkdirSync(path.join(outputDirectory, "renderer", "assets"), { recursive: true });
27
+ fs.copyFileSync(path.join(pluginRoot, "src", "manifest.json"), path.join(outputDirectory, "manifest.json"));
28
+ fs.copyFileSync(path.join(buildDirectory, "main", "main.js"), path.join(outputDirectory, "main.cjs"));
29
+ fs.copyFileSync(path.join(pluginRoot, "src", "renderer", "index.html"), path.join(outputDirectory, "renderer", "index.html"));
30
+ fs.copyFileSync(path.join(pluginRoot, "src", "renderer", "plugin.css"), path.join(outputDirectory, "renderer", "assets", "plugin.css"));
31
+ rewriteRendererSdkImport(
32
+ path.join(buildDirectory, "renderer", "app.js"),
33
+ path.join(outputDirectory, "renderer", "assets", "app.js")
34
+ );
35
+ copyBrowserSdk();
36
+ for (const packageName of runtimePackages) {
37
+ copyRuntimePackage(packageName);
38
+ }
39
+ fs.rmSync(buildDirectory, { recursive: true, force: true });
40
+ console.log("[" + pluginId + "] built " + outputDirectory);
41
+ }
42
+
43
+ function verifyNativeDependency() {
44
+ const packageDirectory = path.join(nodeModulesDirectory, "better-sqlite3");
45
+ if (!fs.existsSync(packageDirectory)) {
46
+ throw new Error("better-sqlite3 is not installed. Run npm install first.");
47
+ }
48
+ const nativeFiles = findFiles(packageDirectory, function isNativeAddon(filePath) {
49
+ return path.basename(filePath).toLowerCase() === "better_sqlite3.node";
50
+ });
51
+ if (nativeFiles.length === 0) {
52
+ throw new Error("better-sqlite3 is not rebuilt. Run npm run rebuild:native first.");
53
+ }
54
+ }
55
+
56
+ function runTypeScript(configName) {
57
+ const tscPath = require.resolve("typescript/bin/tsc");
58
+ const args = [tscPath, "-p", path.join(pluginRoot, configName)];
59
+ if (process.env.NEXUS_PLUGIN_DEV === "1") {
60
+ args.push("--inlineSourceMap", "--inlineSources");
61
+ }
62
+ const result = spawnSync(process.execPath, args, {
63
+ cwd: pluginRoot,
64
+ stdio: "inherit"
65
+ });
66
+ if (result.error) {
67
+ throw result.error;
68
+ }
69
+ if (result.status !== 0) {
70
+ process.exit(result.status || 1);
71
+ }
72
+ }
73
+
74
+ function rewriteRendererSdkImport(sourcePath, outputPath) {
75
+ const sourceText = fs.readFileSync(sourcePath, "utf8");
76
+ const sourceFile = ts.createSourceFile(sourcePath, sourceText, ts.ScriptTarget.ES2020, true, ts.ScriptKind.JS);
77
+ const matches = [];
78
+ sourceFile.forEachChild(function findSdkImport(node) {
79
+ if (ts.isImportDeclaration(node)
80
+ && ts.isStringLiteral(node.moduleSpecifier)
81
+ && node.moduleSpecifier.text === sdkModuleSpecifier) {
82
+ matches.push(node.moduleSpecifier);
83
+ }
84
+ });
85
+ if (matches.length !== 1) {
86
+ throw new Error("Expected one browser SDK import, found " + matches.length + ".");
87
+ }
88
+ const moduleSpecifier = matches[0];
89
+ const rewritten = sourceText.slice(0, moduleSpecifier.getStart(sourceFile))
90
+ + JSON.stringify("./plugin-sdk.mjs")
91
+ + sourceText.slice(moduleSpecifier.getEnd());
92
+ fs.writeFileSync(outputPath, rewritten, "utf8");
93
+ }
94
+
95
+ function copyBrowserSdk() {
96
+ const sdkPackageRoot = path.dirname(require.resolve("@aiot-nexus/plugin-sdk/package.json"));
97
+ const sourcePath = path.join(sdkPackageRoot, "dist", "browser", "frame-client.mjs");
98
+ if (!fs.existsSync(sourcePath)) {
99
+ throw new Error("Plugin SDK browser module is missing: " + sourcePath);
100
+ }
101
+ fs.copyFileSync(sourcePath, path.join(outputDirectory, "renderer", "assets", "plugin-sdk.mjs"));
102
+ }
103
+
104
+ function copyRuntimePackage(packageName) {
105
+ const sourcePath = path.join(nodeModulesDirectory, packageName);
106
+ if (!fs.existsSync(sourcePath)) {
107
+ throw new Error("Runtime package is missing: " + packageName);
108
+ }
109
+ copyDirectory(sourcePath, path.join(outputDirectory, "node_modules", packageName));
110
+ }
111
+
112
+ function copyDirectory(sourceDirectory, targetDirectory) {
113
+ fs.mkdirSync(targetDirectory, { recursive: true });
114
+ for (const entry of fs.readdirSync(sourceDirectory, { withFileTypes: true })) {
115
+ const sourcePath = path.join(sourceDirectory, entry.name);
116
+ const targetPath = path.join(targetDirectory, entry.name);
117
+ if (entry.isSymbolicLink()) {
118
+ throw new Error("Runtime package contains an unsupported symbolic link: " + sourcePath);
119
+ }
120
+ if (entry.isDirectory()) {
121
+ copyDirectory(sourcePath, targetPath);
122
+ continue;
123
+ }
124
+ if (entry.isFile()) {
125
+ fs.copyFileSync(sourcePath, targetPath);
126
+ }
127
+ }
128
+ }
129
+
130
+ function findFiles(directory, predicate) {
131
+ const results = [];
132
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
133
+ const filePath = path.join(directory, entry.name);
134
+ if (entry.isDirectory()) {
135
+ results.push.apply(results, findFiles(filePath, predicate));
136
+ continue;
137
+ }
138
+ if (entry.isFile() && predicate(filePath)) {
139
+ results.push(filePath);
140
+ }
141
+ }
142
+ return results;
143
+ }
@@ -0,0 +1,22 @@
1
+ declare module "better-sqlite3" {
2
+ class Database {
3
+ constructor(filename: string);
4
+ pragma(source: string): unknown;
5
+ prepare(source: string): Database.Statement;
6
+ }
7
+
8
+ namespace Database {
9
+ interface RunResult {
10
+ changes: number;
11
+ lastInsertRowid: number | bigint;
12
+ }
13
+
14
+ interface Statement {
15
+ run(...params: unknown[]): RunResult;
16
+ get(...params: unknown[]): unknown;
17
+ all(...params: unknown[]): unknown[];
18
+ }
19
+ }
20
+
21
+ export = Database;
22
+ }