@arcanejs/toolkit 6.0.1 → 7.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.
- package/README.md +156 -12
- package/dist/backend/components/base.d.mts +1 -1
- package/dist/backend/components/base.d.ts +1 -1
- package/dist/backend/components/button.d.mts +1 -1
- package/dist/backend/components/button.d.ts +1 -1
- package/dist/backend/components/button.js +2 -2
- package/dist/backend/components/button.mjs +1 -1
- package/dist/backend/components/group.d.mts +1 -1
- package/dist/backend/components/group.d.ts +1 -1
- package/dist/backend/components/group.js +2 -2
- package/dist/backend/components/group.mjs +1 -1
- package/dist/backend/components/label.d.mts +2 -2
- package/dist/backend/components/label.d.ts +2 -2
- package/dist/backend/components/label.js +2 -2
- package/dist/backend/components/label.mjs +1 -1
- package/dist/backend/components/rect.d.mts +2 -2
- package/dist/backend/components/rect.d.ts +2 -2
- package/dist/backend/components/rect.js +2 -2
- package/dist/backend/components/rect.mjs +1 -1
- package/dist/backend/components/slider-button.d.mts +2 -2
- package/dist/backend/components/slider-button.d.ts +2 -2
- package/dist/backend/components/slider-button.js +2 -2
- package/dist/backend/components/slider-button.mjs +1 -1
- package/dist/backend/components/switch.d.mts +2 -2
- package/dist/backend/components/switch.d.ts +2 -2
- package/dist/backend/components/switch.js +2 -2
- package/dist/backend/components/switch.mjs +1 -1
- package/dist/backend/components/tabs.d.mts +3 -3
- package/dist/backend/components/tabs.d.ts +3 -3
- package/dist/backend/components/tabs.js +2 -2
- package/dist/backend/components/tabs.mjs +1 -1
- package/dist/backend/components/text-input.d.mts +2 -2
- package/dist/backend/components/text-input.d.ts +2 -2
- package/dist/backend/components/text-input.js +2 -2
- package/dist/backend/components/text-input.mjs +1 -1
- package/dist/backend/components/timeline.d.mts +2 -2
- package/dist/backend/components/timeline.d.ts +2 -2
- package/dist/backend/components/timeline.js +2 -2
- package/dist/backend/components/timeline.mjs +1 -1
- package/dist/{chunk-53EOA4UE.js → chunk-2K4UT5QB.js} +1 -1
- package/dist/{chunk-4OCRZD4F.js → chunk-4AGKM5NT.js} +1 -1
- package/dist/{chunk-QAH2OSHR.js → chunk-5B65Q7RL.js} +1 -1
- package/dist/{chunk-3TXBS2UN.mjs → chunk-6PS3Q66F.mjs} +1 -1
- package/dist/{chunk-LK6MGXYC.mjs → chunk-7MQHRTBE.mjs} +1 -1
- package/dist/{chunk-U6FSQBQ3.js → chunk-A3RWE7HZ.js} +6 -4
- package/dist/{chunk-NL3W4M7J.js → chunk-CVY55KAR.js} +1 -1
- package/dist/{chunk-WDB3IMOP.mjs → chunk-GXJ3JRRK.mjs} +1 -1
- package/dist/{chunk-HTCWMJUA.js → chunk-IV3AE3CW.js} +1 -1
- package/dist/{chunk-UQWCTVMC.mjs → chunk-JHWFQLLE.mjs} +1 -1
- package/dist/{chunk-QB2WPBWV.mjs → chunk-K6V55JTG.mjs} +1 -1
- package/dist/{chunk-PG5EAV5X.mjs → chunk-MHSAHTVN.mjs} +1 -1
- package/dist/{chunk-KMEDXCBI.mjs → chunk-R5OA7LLZ.mjs} +6 -4
- package/dist/{chunk-6ELB27LK.mjs → chunk-RJLYYTZ6.mjs} +1 -1
- package/dist/{chunk-3GTEUTFT.js → chunk-TF7N4O5G.js} +1 -1
- package/dist/{chunk-E6FGU7DA.mjs → chunk-TULW7CRV.mjs} +4 -4
- package/dist/{chunk-6QWYIJLM.js → chunk-UHEZQR2Q.js} +1 -1
- package/dist/{chunk-7JIC2XBC.js → chunk-XSKWLJOQ.js} +4 -4
- package/dist/frontend/entrypoint.css +836 -0
- package/dist/frontend/entrypoint.css.map +7 -0
- package/dist/frontend/entrypoint.js +26048 -26682
- package/dist/frontend/entrypoint.js.map +4 -4
- package/dist/frontend/index.d.mts +4 -6
- package/dist/frontend/index.d.ts +4 -6
- package/dist/frontend/index.js +82 -300
- package/dist/frontend/index.mjs +89 -307
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +280 -98
- package/dist/index.mjs +250 -68
- package/dist/{toolkit-CohVRj6u.d.mts → toolkit-BPqxbDbk.d.mts} +82 -19
- package/dist/{toolkit-C_2Y8N9R.d.ts → toolkit-Bglbv3ix.d.ts} +82 -19
- package/package.json +15 -20
package/dist/index.mjs
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Tab,
|
|
3
3
|
Tabs
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-R5OA7LLZ.mjs";
|
|
5
5
|
import {
|
|
6
6
|
TextInput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-GXJ3JRRK.mjs";
|
|
8
8
|
import {
|
|
9
9
|
Timeline
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6PS3Q66F.mjs";
|
|
11
11
|
import {
|
|
12
12
|
IDMap
|
|
13
13
|
} from "./chunk-RGHJEMWG.mjs";
|
|
14
14
|
import {
|
|
15
15
|
Button
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-7MQHRTBE.mjs";
|
|
17
17
|
import {
|
|
18
18
|
Group,
|
|
19
19
|
GroupHeader
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-TULW7CRV.mjs";
|
|
21
21
|
import {
|
|
22
22
|
Label
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-JHWFQLLE.mjs";
|
|
24
24
|
import {
|
|
25
25
|
Rect
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-RJLYYTZ6.mjs";
|
|
27
27
|
import {
|
|
28
28
|
SliderButton
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-K6V55JTG.mjs";
|
|
30
30
|
import {
|
|
31
31
|
Switch
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-MHSAHTVN.mjs";
|
|
33
33
|
import {
|
|
34
34
|
EventEmitter
|
|
35
35
|
} from "./chunk-GONHNB6V.mjs";
|
|
@@ -43,7 +43,8 @@ import { diffJson } from "@arcanejs/diff/diff";
|
|
|
43
43
|
|
|
44
44
|
// src/backend/options.ts
|
|
45
45
|
var DEFAULT_LIGHT_DESK_OPTIONS = {
|
|
46
|
-
path: "/"
|
|
46
|
+
path: "/",
|
|
47
|
+
clockSync: false
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
// src/backend/server.ts
|
|
@@ -75,93 +76,238 @@ var Server = class {
|
|
|
75
76
|
this.onClosedConnection = onClosedConnection;
|
|
76
77
|
this.onMessage = onMessage;
|
|
77
78
|
this.log = log;
|
|
79
|
+
this.title = options.title ?? "@arcanejs";
|
|
80
|
+
}
|
|
81
|
+
staticFiles = null;
|
|
82
|
+
htmlContext = null;
|
|
83
|
+
staticFilesInitPromise = null;
|
|
84
|
+
title;
|
|
85
|
+
getEntrypointPaths = () => {
|
|
78
86
|
const entrypoint = this.options.entrypointJsFile ?? path.join(distDir(), "frontend", "entrypoint.js");
|
|
79
87
|
if (!entrypoint.endsWith(".js")) {
|
|
80
88
|
throw new Error("Entrypoint file must be a .js file");
|
|
81
89
|
}
|
|
82
90
|
const entrypointMap = entrypoint + ".map";
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
[`/${path.basename(entrypointMap)}`]: {
|
|
95
|
-
path: entrypointMap,
|
|
96
|
-
contentType: "text/plain"
|
|
97
|
-
}
|
|
91
|
+
const entrypointCss = entrypoint.replace(/\.js$/, ".css");
|
|
92
|
+
const entrypointCssMap = entrypointCss + ".map";
|
|
93
|
+
return {
|
|
94
|
+
entrypoint,
|
|
95
|
+
entrypointMap,
|
|
96
|
+
entrypointCss,
|
|
97
|
+
entrypointCssMap,
|
|
98
|
+
entrypointFilename: path.basename(entrypoint),
|
|
99
|
+
entrypointMapFilename: path.basename(entrypointMap),
|
|
100
|
+
entrypointCssFilename: path.basename(entrypointCss),
|
|
101
|
+
entrypointCssMapFilename: path.basename(entrypointCssMap)
|
|
98
102
|
};
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
103
|
+
};
|
|
104
|
+
fileExists = async (filePath) => {
|
|
105
|
+
try {
|
|
106
|
+
await fs.promises.access(filePath, fs.constants.F_OK);
|
|
107
|
+
return true;
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
ensureStaticFilesInitialized = async () => {
|
|
113
|
+
if (this.staticFiles && this.htmlContext) return;
|
|
114
|
+
if (this.staticFilesInitPromise) return this.staticFilesInitPromise;
|
|
115
|
+
this.staticFilesInitPromise = (async () => {
|
|
116
|
+
const {
|
|
117
|
+
entrypoint,
|
|
118
|
+
entrypointMap,
|
|
119
|
+
entrypointCss,
|
|
120
|
+
entrypointCssMap,
|
|
121
|
+
entrypointFilename,
|
|
122
|
+
entrypointMapFilename,
|
|
123
|
+
entrypointCssFilename,
|
|
124
|
+
entrypointCssMapFilename
|
|
125
|
+
} = this.getEntrypointPaths();
|
|
126
|
+
const [hasEntrypointMap, hasEntrypointCss, hasEntrypointCssMap] = await Promise.all([
|
|
127
|
+
this.fileExists(entrypointMap),
|
|
128
|
+
this.fileExists(entrypointCss),
|
|
129
|
+
this.fileExists(entrypointCssMap)
|
|
130
|
+
]);
|
|
131
|
+
const staticFilePaths = {
|
|
132
|
+
materialSymbolsOutlined: FONTS.materialSymbolsOutlined,
|
|
133
|
+
entrypointJs: entrypointFilename,
|
|
134
|
+
...hasEntrypointMap ? { entrypointJsMap: entrypointMapFilename } : {},
|
|
135
|
+
...hasEntrypointCss ? { entrypointCss: entrypointCssFilename } : {},
|
|
136
|
+
...hasEntrypointCssMap ? { entrypointCssMap: entrypointCssMapFilename } : {}
|
|
137
|
+
};
|
|
138
|
+
const additionalFiles = this.options.additionalFiles ?? {};
|
|
139
|
+
const additionalFileEntries = Object.entries(additionalFiles).map(
|
|
140
|
+
([relativePath, loader]) => [
|
|
141
|
+
this.toRoutePath(relativePath),
|
|
142
|
+
async () => {
|
|
143
|
+
const asset = await loader();
|
|
144
|
+
if (!Buffer.isBuffer(asset.content)) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
`Additional file "${relativePath}" did not return a Buffer in content`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return asset;
|
|
150
|
+
}
|
|
151
|
+
]
|
|
152
|
+
);
|
|
153
|
+
this.staticFiles = {
|
|
154
|
+
[this.toRoutePath(staticFilePaths.materialSymbolsOutlined)]: async () => ({
|
|
155
|
+
content: await fs.promises.readFile(
|
|
156
|
+
this.options.materialIconsFontFile ?? __require.resolve(
|
|
157
|
+
"material-symbols/material-symbols-outlined.woff2"
|
|
158
|
+
)
|
|
159
|
+
),
|
|
160
|
+
contentType: "font/woff2"
|
|
161
|
+
}),
|
|
162
|
+
[this.toRoutePath(staticFilePaths.entrypointJs)]: async () => ({
|
|
163
|
+
content: await fs.promises.readFile(entrypoint),
|
|
164
|
+
contentType: "text/javascript"
|
|
165
|
+
}),
|
|
166
|
+
...hasEntrypointMap ? {
|
|
167
|
+
[this.toRoutePath(entrypointMapFilename)]: async () => ({
|
|
168
|
+
content: await fs.promises.readFile(entrypointMap),
|
|
169
|
+
contentType: "text/plain"
|
|
170
|
+
})
|
|
171
|
+
} : {},
|
|
172
|
+
...hasEntrypointCss ? {
|
|
173
|
+
[this.toRoutePath(entrypointCssFilename)]: async () => ({
|
|
174
|
+
content: await fs.promises.readFile(entrypointCss),
|
|
175
|
+
contentType: "text/css"
|
|
176
|
+
})
|
|
177
|
+
} : {},
|
|
178
|
+
...hasEntrypointCssMap ? {
|
|
179
|
+
[this.toRoutePath(entrypointCssMapFilename)]: async () => ({
|
|
180
|
+
content: await fs.promises.readFile(entrypointCssMap),
|
|
181
|
+
contentType: "text/plain"
|
|
182
|
+
})
|
|
183
|
+
} : {},
|
|
184
|
+
...Object.fromEntries(additionalFileEntries)
|
|
185
|
+
};
|
|
186
|
+
this.htmlContext = {
|
|
187
|
+
title: this.title,
|
|
188
|
+
path: this.options.path,
|
|
189
|
+
coreAssets: {
|
|
190
|
+
materialSymbolsOutlined: this.toSiteRelativeUrl(
|
|
191
|
+
staticFilePaths.materialSymbolsOutlined
|
|
192
|
+
),
|
|
193
|
+
entrypointJs: this.toSiteRelativeUrl(staticFilePaths.entrypointJs),
|
|
194
|
+
entrypointJsMap: hasEntrypointMap ? this.toSiteRelativeUrl(entrypointMapFilename) : null,
|
|
195
|
+
entrypointCss: hasEntrypointCss ? this.toSiteRelativeUrl(entrypointCssFilename) : null,
|
|
196
|
+
entrypointCssMap: hasEntrypointCssMap ? this.toSiteRelativeUrl(entrypointCssMapFilename) : null
|
|
197
|
+
},
|
|
198
|
+
assetUrls: this.createAssetUrls(this.staticFiles)
|
|
199
|
+
};
|
|
200
|
+
this.log?.debug("Static Assets: %o", this.staticFiles);
|
|
201
|
+
})();
|
|
202
|
+
this.staticFilesInitPromise.catch(() => {
|
|
203
|
+
this.staticFilesInitPromise = null;
|
|
204
|
+
this.staticFiles = null;
|
|
205
|
+
this.htmlContext = null;
|
|
206
|
+
});
|
|
207
|
+
return this.staticFilesInitPromise;
|
|
208
|
+
};
|
|
104
209
|
handleHttpRequest = async (req, res) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
210
|
+
try {
|
|
211
|
+
await this.ensureStaticFilesInitialized();
|
|
212
|
+
} catch (err) {
|
|
213
|
+
if (err instanceof Error) {
|
|
214
|
+
this.log?.error(err);
|
|
215
|
+
} else {
|
|
216
|
+
this.log?.error("Error preparing static files: %o", err);
|
|
217
|
+
}
|
|
218
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
219
|
+
res.end("Unable to prepare static files", "utf-8");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const htmlContext = this.htmlContext;
|
|
223
|
+
const staticFiles = this.staticFiles;
|
|
224
|
+
if (!htmlContext || !staticFiles) {
|
|
225
|
+
throw new Error("Static assets were not initialized");
|
|
226
|
+
}
|
|
227
|
+
const requestUrl = req.originalUrl ?? req.url;
|
|
228
|
+
this.log?.debug("handleHttpRequest %s", requestUrl);
|
|
229
|
+
const pathname = this.parsePathname(requestUrl);
|
|
230
|
+
if (pathname === this.options.path) {
|
|
231
|
+
const content = await this.options.htmlPage?.(htmlContext) ?? `
|
|
108
232
|
<html>
|
|
109
233
|
<head>
|
|
110
|
-
<title>${
|
|
234
|
+
<title>${escapeHTML(htmlContext.title)}</title>
|
|
111
235
|
<meta charset="utf-8">
|
|
112
236
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
113
237
|
<style type="text/css">
|
|
114
238
|
@font-face {
|
|
115
239
|
font-family: 'Material Symbols Outlined';
|
|
116
240
|
font-style: normal;
|
|
117
|
-
src: url(${
|
|
241
|
+
src: url(${htmlContext.coreAssets.materialSymbolsOutlined}) format('woff');
|
|
118
242
|
}
|
|
119
243
|
</style>
|
|
244
|
+
${htmlContext.coreAssets.entrypointCss ? `<link rel="stylesheet" href="${htmlContext.coreAssets.entrypointCss}" />` : ""}
|
|
120
245
|
</head>
|
|
121
246
|
<body>
|
|
122
247
|
<div id="root"></div>
|
|
123
|
-
<script type="text/javascript" src="${
|
|
248
|
+
<script type="text/javascript" src="${htmlContext.coreAssets.entrypointJs}"></script>
|
|
124
249
|
</body>
|
|
125
250
|
</html>`;
|
|
126
251
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
127
252
|
res.end(content, "utf-8");
|
|
128
253
|
return;
|
|
129
254
|
}
|
|
130
|
-
if (
|
|
131
|
-
const relativePath =
|
|
132
|
-
const f =
|
|
255
|
+
if (pathname && pathname.startsWith(this.options.path)) {
|
|
256
|
+
const relativePath = pathname.substr(this.options.path.length - 1);
|
|
257
|
+
const f = staticFiles[relativePath];
|
|
133
258
|
if (f) {
|
|
134
|
-
|
|
135
|
-
()
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
259
|
+
try {
|
|
260
|
+
const response = await f();
|
|
261
|
+
res.writeHead(200, { "Content-Type": response.contentType });
|
|
262
|
+
res.end(response.content);
|
|
263
|
+
return;
|
|
264
|
+
} catch (err) {
|
|
265
|
+
if (err instanceof Error) {
|
|
139
266
|
this.log?.error(err);
|
|
140
|
-
|
|
141
|
-
|
|
267
|
+
} else {
|
|
268
|
+
this.log?.error("Error loading static file: %o", err);
|
|
142
269
|
}
|
|
143
|
-
|
|
270
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
271
|
+
res.end("Expected static file not found", "utf-8");
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
144
274
|
}
|
|
145
275
|
}
|
|
146
276
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
147
277
|
res.end("not found", "utf-8");
|
|
148
278
|
};
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
279
|
+
parsePathname = (url) => {
|
|
280
|
+
if (!url) return "";
|
|
281
|
+
try {
|
|
282
|
+
return new URL(url, "http://127.0.0.1").pathname;
|
|
283
|
+
} catch {
|
|
284
|
+
return url;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
toRoutePath = (relativePath) => {
|
|
288
|
+
if (!relativePath) {
|
|
289
|
+
throw new Error("Static file path must be non-empty");
|
|
290
|
+
}
|
|
291
|
+
if (relativePath.startsWith("/")) {
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Static file path "${relativePath}" must be relative and not start with "/"`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return `/${relativePath}`;
|
|
297
|
+
};
|
|
298
|
+
toSiteRelativeUrl = (relativePath) => {
|
|
299
|
+
const normalizedPath = relativePath.replace(/^\/+/, "");
|
|
300
|
+
return `${this.options.path}${normalizedPath}`;
|
|
301
|
+
};
|
|
302
|
+
createAssetUrls = (staticFiles) => {
|
|
303
|
+
const urls = {};
|
|
304
|
+
for (const routePath of Object.keys(staticFiles)) {
|
|
305
|
+
const relativePath = routePath.replace(/^\/+/, "");
|
|
306
|
+
urls[relativePath] = this.toSiteRelativeUrl(
|
|
307
|
+
relativePath
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
return urls;
|
|
165
311
|
};
|
|
166
312
|
handleWsConnection = (ws) => {
|
|
167
313
|
const connection = {
|
|
@@ -181,8 +327,23 @@ var Server = class {
|
|
|
181
327
|
import { WebSocketServer } from "ws";
|
|
182
328
|
import { createServer } from "http";
|
|
183
329
|
import { v4 as uuidv4 } from "uuid";
|
|
330
|
+
var normalizeClockSyncOptions = (clockSync) => {
|
|
331
|
+
if (!clockSync) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
const { pingIntervalMs } = clockSync;
|
|
335
|
+
if (!Number.isFinite(pingIntervalMs) || pingIntervalMs <= 0) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
`clockSync.pingIntervalMs must be a positive number, got: ${pingIntervalMs}`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
pingIntervalMs
|
|
342
|
+
};
|
|
343
|
+
};
|
|
184
344
|
var Toolkit = class {
|
|
185
345
|
options;
|
|
346
|
+
clockSync;
|
|
186
347
|
/**
|
|
187
348
|
* Mapping from components to unique IDs that identify them
|
|
188
349
|
*/
|
|
@@ -197,6 +358,7 @@ var Toolkit = class {
|
|
|
197
358
|
...DEFAULT_LIGHT_DESK_OPTIONS,
|
|
198
359
|
...options
|
|
199
360
|
};
|
|
361
|
+
this.clockSync = normalizeClockSyncOptions(this.options.clockSync);
|
|
200
362
|
if (!this.options.path.endsWith("/") || !this.options.path.startsWith("/")) {
|
|
201
363
|
throw new Error(
|
|
202
364
|
`path must start and end with "/", set to: ${this.options.path}`
|
|
@@ -283,11 +445,15 @@ var Toolkit = class {
|
|
|
283
445
|
() => {
|
|
284
446
|
setImmediate(() => {
|
|
285
447
|
if (!this.rootGroup) return;
|
|
286
|
-
const root = this.rootGroup.getProtoInfo(this.componentIDMap);
|
|
287
448
|
for (const [connection, meta] of this.connections.entries()) {
|
|
449
|
+
const root = this.rootGroup.getProtoInfo(this.componentIDMap, {
|
|
450
|
+
connection: meta.publicConnection
|
|
451
|
+
});
|
|
452
|
+
const diff = diffJson(meta.lastTreeSent, root);
|
|
453
|
+
if (diff.type === "match") continue;
|
|
288
454
|
connection.sendMessage({
|
|
289
455
|
type: "tree-diff",
|
|
290
|
-
diff
|
|
456
|
+
diff
|
|
291
457
|
});
|
|
292
458
|
meta.lastTreeSent = root;
|
|
293
459
|
}
|
|
@@ -303,18 +469,26 @@ var Toolkit = class {
|
|
|
303
469
|
}
|
|
304
470
|
};
|
|
305
471
|
onNewConnection = (connection) => {
|
|
306
|
-
const lastTreeSent = this.rootGroup?.getProtoInfo(this.componentIDMap) ?? void 0;
|
|
307
472
|
const uuid = uuidv4();
|
|
308
473
|
const publicConnection = {
|
|
309
474
|
get uuid() {
|
|
310
475
|
return uuid;
|
|
311
476
|
}
|
|
312
477
|
};
|
|
313
|
-
this.
|
|
478
|
+
const lastTreeSent = this.rootGroup?.getProtoInfo(this.componentIDMap, {
|
|
479
|
+
connection: publicConnection
|
|
480
|
+
}) ?? void 0;
|
|
481
|
+
this.connections.set(connection, {
|
|
482
|
+
publicConnection,
|
|
483
|
+
lastTreeSent
|
|
484
|
+
});
|
|
314
485
|
this.events.emit("new-connection", publicConnection);
|
|
315
486
|
connection.sendMessage({
|
|
316
487
|
type: "metadata",
|
|
317
|
-
connectionUuid: uuid
|
|
488
|
+
connectionUuid: uuid,
|
|
489
|
+
clockSync: this.clockSync ? {
|
|
490
|
+
pingIntervalMs: this.clockSync.pingIntervalMs
|
|
491
|
+
} : null
|
|
318
492
|
});
|
|
319
493
|
if (lastTreeSent) {
|
|
320
494
|
connection.sendMessage({
|
|
@@ -385,6 +559,14 @@ var Toolkit = class {
|
|
|
385
559
|
case "component-call":
|
|
386
560
|
this.handleCall(connection, publicConnection, message);
|
|
387
561
|
break;
|
|
562
|
+
case "ping": {
|
|
563
|
+
connection.sendMessage({
|
|
564
|
+
type: "pong",
|
|
565
|
+
pingId: message.pingId,
|
|
566
|
+
serverTimeMillis: Date.now()
|
|
567
|
+
});
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
388
570
|
}
|
|
389
571
|
};
|
|
390
572
|
};
|
|
@@ -12,22 +12,58 @@ import { I as IDMap } from './id-map-DxQ3_gyA.mjs';
|
|
|
12
12
|
interface Connection {
|
|
13
13
|
sendMessage(msg: ServerMessage): void;
|
|
14
14
|
}
|
|
15
|
-
declare class Server {
|
|
15
|
+
declare class Server<TAdditionalFiles extends ToolkitAdditionalFiles = Record<never, never>> {
|
|
16
16
|
private readonly options;
|
|
17
17
|
private readonly onNewConnection;
|
|
18
18
|
private readonly onClosedConnection;
|
|
19
19
|
private readonly onMessage;
|
|
20
20
|
private readonly log?;
|
|
21
|
-
private
|
|
22
|
-
private
|
|
23
|
-
private
|
|
24
|
-
|
|
21
|
+
private staticFiles;
|
|
22
|
+
private htmlContext;
|
|
23
|
+
private staticFilesInitPromise;
|
|
24
|
+
private readonly title;
|
|
25
|
+
constructor(options: ToolkitOptions<TAdditionalFiles>, onNewConnection: (connection: Connection) => void, onClosedConnection: (connection: Connection) => void, onMessage: (connection: Connection, message: ClientMessage) => void, log?: Logger | undefined);
|
|
26
|
+
private getEntrypointPaths;
|
|
27
|
+
private fileExists;
|
|
28
|
+
private ensureStaticFilesInitialized;
|
|
25
29
|
handleHttpRequest: (req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
|
|
26
|
-
private
|
|
30
|
+
private parsePathname;
|
|
31
|
+
private toRoutePath;
|
|
32
|
+
private toSiteRelativeUrl;
|
|
33
|
+
private createAssetUrls;
|
|
27
34
|
handleWsConnection: <S extends WebSocket>(ws: S) => void;
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
declare const FONTS: {
|
|
38
|
+
readonly materialSymbolsOutlined: "material-symbols-outlined.woff2";
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type ToolkitStaticFile = {
|
|
42
|
+
contentType: string;
|
|
43
|
+
content: Buffer;
|
|
44
|
+
};
|
|
45
|
+
type ToolkitStaticFileResolver = () => Promise<ToolkitStaticFile>;
|
|
46
|
+
type ToolkitAdditionalFiles = Record<string, ToolkitStaticFileResolver>;
|
|
47
|
+
type ToolkitCoreAssetRelativePath = typeof FONTS.materialSymbolsOutlined | `${string}.js` | `${string}.js.map` | `${string}.css` | `${string}.css.map`;
|
|
48
|
+
type ToolkitHtmlPageContext<TAdditionalFiles extends ToolkitAdditionalFiles> = {
|
|
49
|
+
title: string;
|
|
50
|
+
path: string;
|
|
51
|
+
coreAssets: {
|
|
52
|
+
materialSymbolsOutlined: string;
|
|
53
|
+
entrypointJs: string;
|
|
54
|
+
entrypointJsMap: string | null;
|
|
55
|
+
entrypointCss: string | null;
|
|
56
|
+
entrypointCssMap: string | null;
|
|
57
|
+
};
|
|
58
|
+
assetUrls: Record<ToolkitCoreAssetRelativePath | Extract<keyof TAdditionalFiles, string>, string>;
|
|
59
|
+
};
|
|
60
|
+
type ToolkitClockSyncOptions = {
|
|
61
|
+
/**
|
|
62
|
+
* How often the browser should request a ping in milliseconds.
|
|
63
|
+
*/
|
|
64
|
+
pingIntervalMs: number;
|
|
65
|
+
};
|
|
66
|
+
interface ToolkitOptions<TAdditionalFiles extends ToolkitAdditionalFiles = Record<never, never>> {
|
|
31
67
|
/**
|
|
32
68
|
* What window title should the toolkit be initialized with?
|
|
33
69
|
*/
|
|
@@ -53,8 +89,10 @@ interface ToolkitOptions {
|
|
|
53
89
|
* This is only needed if you have defined custom extensions,
|
|
54
90
|
* and need to load custom frontend code that includes your extensions.
|
|
55
91
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
92
|
+
* ArcaneJS expects an associated `.css` file with the same basename
|
|
93
|
+
* (for example `entrypoint.js` + `entrypoint.css`) so browser styles are
|
|
94
|
+
* served automatically. Source map files (`.js.map`, `.css.map`) are optional,
|
|
95
|
+
* and exposed when present.
|
|
58
96
|
*/
|
|
59
97
|
entrypointJsFile?: string;
|
|
60
98
|
/**
|
|
@@ -64,8 +102,29 @@ interface ToolkitOptions {
|
|
|
64
102
|
* you can provide the path to the material-symbols-outlined.woff2 file here.
|
|
65
103
|
*/
|
|
66
104
|
materialIconsFontFile?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Additional static assets that should be exposed from the toolkit path.
|
|
107
|
+
*
|
|
108
|
+
* The object key is the relative request path (for example `styles/app.css`),
|
|
109
|
+
* and the loader returns the response payload and content type.
|
|
110
|
+
*/
|
|
111
|
+
additionalFiles?: TAdditionalFiles;
|
|
112
|
+
/**
|
|
113
|
+
* Optional custom HTML renderer for the root page.
|
|
114
|
+
*
|
|
115
|
+
* Receives resolved site-relative URLs for all available static assets,
|
|
116
|
+
* including toolkit defaults and files provided by `additionalFiles`.
|
|
117
|
+
*/
|
|
118
|
+
htmlPage?: (context: ToolkitHtmlPageContext<TAdditionalFiles>) => string | Promise<string>;
|
|
119
|
+
/**
|
|
120
|
+
* Enable browser/server clock synchronization support.
|
|
121
|
+
*
|
|
122
|
+
* When enabled, frontend clients will periodically ping the toolkit server
|
|
123
|
+
* and expose a calculated server clock offset in stage context.
|
|
124
|
+
*/
|
|
125
|
+
clockSync?: false | ToolkitClockSyncOptions;
|
|
67
126
|
}
|
|
68
|
-
type InitializationOptions =
|
|
127
|
+
type InitializationOptions<TAdditionalFiles extends ToolkitAdditionalFiles = Record<never, never>> =
|
|
69
128
|
/** automatically start a simple */
|
|
70
129
|
{
|
|
71
130
|
mode: 'automatic';
|
|
@@ -85,7 +144,7 @@ type InitializationOptions =
|
|
|
85
144
|
/** Create a websocket server that attaches to an existing express and http server */
|
|
86
145
|
| {
|
|
87
146
|
mode: 'manual';
|
|
88
|
-
setup: (server: Server) => void;
|
|
147
|
+
setup: (server: Server<TAdditionalFiles>) => void;
|
|
89
148
|
};
|
|
90
149
|
|
|
91
150
|
declare abstract class Base<Namespace extends string, Proto extends BaseComponentProto<Namespace, string>, Props, CallPairs = any, CallActions extends string & keyof CallPairs = any> {
|
|
@@ -114,7 +173,7 @@ declare abstract class Base<Namespace extends string, Proto extends BaseComponen
|
|
|
114
173
|
/** @hidden */
|
|
115
174
|
updateTree(): void;
|
|
116
175
|
/** @hidden */
|
|
117
|
-
abstract getProtoInfo(idMap: IDMap): Proto;
|
|
176
|
+
abstract getProtoInfo(idMap: IDMap, context: ToolkitRenderContext): Proto;
|
|
118
177
|
/** @hidden */
|
|
119
178
|
handleMessage(_message: AnyClientComponentMessage, _connection: ToolkitConnection): void;
|
|
120
179
|
/** @hidden */
|
|
@@ -199,7 +258,7 @@ declare class Button extends Base<proto.CoreNamespace, proto.ButtonComponent, In
|
|
|
199
258
|
setIcon: (icon: string | undefined | null) => this;
|
|
200
259
|
setMode: (mode: ButtonMode) => this;
|
|
201
260
|
/** @hidden */
|
|
202
|
-
getProtoInfo: (idMap: IDMap) => proto.ButtonComponent;
|
|
261
|
+
getProtoInfo: (idMap: IDMap, _context: ToolkitRenderContext) => proto.ButtonComponent;
|
|
203
262
|
/** @hidden */
|
|
204
263
|
handleCall: (call: AnyClientComponentCall, connection: ToolkitConnection) => Promise<true>;
|
|
205
264
|
}
|
|
@@ -221,7 +280,7 @@ type Props = Partial<InternalProps>;
|
|
|
221
280
|
declare class GroupHeader extends BaseParent<proto.CoreNamespace, proto.CoreComponent, Record<never, never>> {
|
|
222
281
|
validateChildren: () => void;
|
|
223
282
|
/** @hidden */
|
|
224
|
-
getProtoInfo: (idMap: IDMap) => proto.GroupHeaderComponent;
|
|
283
|
+
getProtoInfo: (idMap: IDMap, context: ToolkitRenderContext) => proto.GroupHeaderComponent;
|
|
225
284
|
}
|
|
226
285
|
/**
|
|
227
286
|
* A collection of components, grouped in either a row or column. Can contain
|
|
@@ -245,7 +304,7 @@ declare class Group extends BaseParent<proto.CoreNamespace, proto.GroupComponent
|
|
|
245
304
|
removeHeaderChild: (child: Button) => void;
|
|
246
305
|
removeAllHeaderChildren: () => void;
|
|
247
306
|
/** @hidden */
|
|
248
|
-
getProtoInfo: (idMap: IDMap) => proto.GroupComponent;
|
|
307
|
+
getProtoInfo: (idMap: IDMap, context: ToolkitRenderContext) => proto.GroupComponent;
|
|
249
308
|
/** @hidden */
|
|
250
309
|
handleMessage: (message: AnyClientComponentMessage, connection: ToolkitConnection) => void;
|
|
251
310
|
}
|
|
@@ -253,6 +312,9 @@ declare class Group extends BaseParent<proto.CoreNamespace, proto.GroupComponent
|
|
|
253
312
|
type ToolkitConnection = {
|
|
254
313
|
uuid: string;
|
|
255
314
|
};
|
|
315
|
+
type ToolkitRenderContext = {
|
|
316
|
+
connection: ToolkitConnection;
|
|
317
|
+
};
|
|
256
318
|
type Events = {
|
|
257
319
|
'new-connection': (connection: ToolkitConnection) => void;
|
|
258
320
|
'closed-connection': (connection: ToolkitConnection) => void;
|
|
@@ -264,8 +326,9 @@ type ToolkitServerListenerOptions = {
|
|
|
264
326
|
type ToolkitServerListener = {
|
|
265
327
|
close: () => void;
|
|
266
328
|
};
|
|
267
|
-
declare class Toolkit implements Parent, Listenable<Events> {
|
|
329
|
+
declare class Toolkit<TAdditionalFiles extends ToolkitAdditionalFiles = Record<never, never>> implements Parent, Listenable<Events> {
|
|
268
330
|
private readonly options;
|
|
331
|
+
private readonly clockSync;
|
|
269
332
|
/**
|
|
270
333
|
* Mapping from components to unique IDs that identify them
|
|
271
334
|
*/
|
|
@@ -275,10 +338,10 @@ declare class Toolkit implements Parent, Listenable<Events> {
|
|
|
275
338
|
/** @hidden */
|
|
276
339
|
private readonly events;
|
|
277
340
|
private readonly server;
|
|
278
|
-
constructor(options?: Partial<ToolkitOptions
|
|
341
|
+
constructor(options?: Partial<ToolkitOptions<TAdditionalFiles>>);
|
|
279
342
|
addListener: <T extends keyof Events>(type: T, listener: Events[T]) => void;
|
|
280
343
|
removeListener: <T extends keyof Events>(type: T, listener: Events[T]) => void;
|
|
281
|
-
start: (opts: InitializationOptions) => void;
|
|
344
|
+
start: (opts: InitializationOptions<TAdditionalFiles>) => void;
|
|
282
345
|
listen: ({ port, host, }: ToolkitServerListenerOptions) => Promise<ToolkitServerListener>;
|
|
283
346
|
setRoot: (group: Group) => void;
|
|
284
347
|
log(): _arcanejs_protocol_logging.Logger | null;
|
|
@@ -291,4 +354,4 @@ declare class Toolkit implements Parent, Listenable<Events> {
|
|
|
291
354
|
private onMessage;
|
|
292
355
|
}
|
|
293
356
|
|
|
294
|
-
export { type AnyComponent as A, Button as B, EventEmitter as E, Group as G, type InternalProps$1 as I, type Listenable as L, type Parent as P, Toolkit as T, type ToolkitConnection as a, type
|
|
357
|
+
export { type AnyComponent as A, Button as B, EventEmitter as E, Group as G, type InternalProps$1 as I, type Listenable as L, type Parent as P, Toolkit as T, type ToolkitConnection as a, type ToolkitRenderContext as b, type ToolkitServerListenerOptions as c, type ToolkitServerListener as d, type ToolkitAdditionalFiles as e, type ToolkitClockSyncOptions as f, type ToolkitCoreAssetRelativePath as g, type ToolkitHtmlPageContext as h, type ToolkitOptions as i, type ToolkitStaticFile as j, type ToolkitStaticFileResolver as k, GroupHeader as l, Base as m, BaseParent as n, type Events$2 as o, type ButtonMode as p, type Props$1 as q, type Events$1 as r, type InternalProps as s, type Props as t };
|