@virtualdisplay.io/client 3.5.0 → 3.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +84 -1
- package/dist/virtualdisplay.client.cjs.js +3 -3
- package/dist/virtualdisplay.client.cjs.js.map +1 -1
- package/dist/virtualdisplay.client.es.js +175 -15
- package/dist/virtualdisplay.client.es.js.map +1 -1
- package/dist/virtualdisplay.client.umd.js +3 -3
- package/dist/virtualdisplay.client.umd.js.map +1 -1
- package/package.json +10 -10
|
@@ -4,7 +4,8 @@ const ERROR_CODES = {
|
|
|
4
4
|
VALUE_NOT_FOUND: "VALUE_NOT_FOUND",
|
|
5
5
|
INVALID_MAPPING: "INVALID_MAPPING",
|
|
6
6
|
PARENT_NOT_FOUND: "PARENT_NOT_FOUND",
|
|
7
|
-
INVALID_MUTATIONS: "INVALID_MUTATIONS"
|
|
7
|
+
INVALID_MUTATIONS: "INVALID_MUTATIONS",
|
|
8
|
+
INVALID_PARAMETER: "INVALID_PARAMETER"
|
|
8
9
|
};
|
|
9
10
|
class VirtualdisplayError extends Error {
|
|
10
11
|
code;
|
|
@@ -61,7 +62,10 @@ const EVENT_NAMES = {
|
|
|
61
62
|
// State flow (Server → Client)
|
|
62
63
|
MESSAGE_RECEIVED: "message:received",
|
|
63
64
|
STATE_CHANGED: "state:changed",
|
|
64
|
-
INITIAL_STATE_RECEIVED: "state:initial-received"
|
|
65
|
+
INITIAL_STATE_RECEIVED: "state:initial-received",
|
|
66
|
+
// Snapshot flow
|
|
67
|
+
SNAPSHOT_MESSAGE: "snapshot:take",
|
|
68
|
+
SNAPSHOT_DEVELOPED: "snapshot:developed"
|
|
65
69
|
};
|
|
66
70
|
const g = Object.freeze({
|
|
67
71
|
debug: 0,
|
|
@@ -6931,7 +6935,8 @@ const MESSAGE_TYPES = {
|
|
|
6931
6935
|
MUTATION: "mutation",
|
|
6932
6936
|
STATE: "state",
|
|
6933
6937
|
CONFIG: "config",
|
|
6934
|
-
CAMERA: "camera"
|
|
6938
|
+
CAMERA: "camera",
|
|
6939
|
+
SNAPSHOT: "snapshot"
|
|
6935
6940
|
};
|
|
6936
6941
|
function createMutationMessage(mutations) {
|
|
6937
6942
|
if (!Array.isArray(mutations)) {
|
|
@@ -6943,14 +6948,7 @@ function createMutationMessage(mutations) {
|
|
|
6943
6948
|
};
|
|
6944
6949
|
}
|
|
6945
6950
|
function isValidMessage(data) {
|
|
6946
|
-
return
|
|
6947
|
-
}
|
|
6948
|
-
function isMutationMessage(data) {
|
|
6949
|
-
if (typeof data !== "object" || data === null) {
|
|
6950
|
-
return false;
|
|
6951
|
-
}
|
|
6952
|
-
const obj = data;
|
|
6953
|
-
return obj.type === MESSAGE_TYPES.MUTATION && Array.isArray(obj.mutations);
|
|
6951
|
+
return isStateMessage(data) || isSnapshotDevelopedMessage(data);
|
|
6954
6952
|
}
|
|
6955
6953
|
function isStateMessage(data) {
|
|
6956
6954
|
if (typeof data !== "object" || data === null) {
|
|
@@ -6959,12 +6957,12 @@ function isStateMessage(data) {
|
|
|
6959
6957
|
const obj = data;
|
|
6960
6958
|
return obj.type === MESSAGE_TYPES.STATE && Array.isArray(obj.nodes);
|
|
6961
6959
|
}
|
|
6962
|
-
function
|
|
6960
|
+
function isSnapshotDevelopedMessage(data) {
|
|
6963
6961
|
if (typeof data !== "object" || data === null) {
|
|
6964
6962
|
return false;
|
|
6965
6963
|
}
|
|
6966
6964
|
const obj = data;
|
|
6967
|
-
return obj.type === MESSAGE_TYPES.
|
|
6965
|
+
return obj.type === MESSAGE_TYPES.SNAPSHOT && typeof obj.filename === "string" && typeof obj.data === "string";
|
|
6968
6966
|
}
|
|
6969
6967
|
class AttributeService {
|
|
6970
6968
|
attributes = /* @__PURE__ */ new Map();
|
|
@@ -7227,11 +7225,13 @@ class MessageHandler {
|
|
|
7227
7225
|
const outgoingEvents = [
|
|
7228
7226
|
EVENT_NAMES.MUTATION_MESSAGE,
|
|
7229
7227
|
EVENT_NAMES.CONFIG_MESSAGE,
|
|
7230
|
-
EVENT_NAMES.CAMERA_MESSAGE
|
|
7228
|
+
EVENT_NAMES.CAMERA_MESSAGE,
|
|
7229
|
+
EVENT_NAMES.SNAPSHOT_MESSAGE
|
|
7231
7230
|
];
|
|
7232
7231
|
outgoingEvents.forEach((eventName) => {
|
|
7233
7232
|
this.eventBus.on(eventName, (event) => {
|
|
7234
|
-
|
|
7233
|
+
const messageEvent = event;
|
|
7234
|
+
this.queueOrSend(messageEvent.message);
|
|
7235
7235
|
});
|
|
7236
7236
|
});
|
|
7237
7237
|
}
|
|
@@ -7419,6 +7419,156 @@ class StateService {
|
|
|
7419
7419
|
this.hasReceivedInitialSync = false;
|
|
7420
7420
|
}
|
|
7421
7421
|
}
|
|
7422
|
+
class Photo {
|
|
7423
|
+
constructor(filename) {
|
|
7424
|
+
this.filename = filename;
|
|
7425
|
+
}
|
|
7426
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
7427
|
+
developed = false;
|
|
7428
|
+
photoData = null;
|
|
7429
|
+
/**
|
|
7430
|
+
* Register a callback for when the photo is developed
|
|
7431
|
+
* If the photo is already developed, the callback is called immediately
|
|
7432
|
+
*
|
|
7433
|
+
* @param callback Function to call with photo data
|
|
7434
|
+
* @returns This Photo instance for chaining
|
|
7435
|
+
*/
|
|
7436
|
+
onDeveloped(callback) {
|
|
7437
|
+
if (this.developed && this.photoData !== null) {
|
|
7438
|
+
callback(this.photoData);
|
|
7439
|
+
return this;
|
|
7440
|
+
}
|
|
7441
|
+
this.callbacks.add(callback);
|
|
7442
|
+
return this;
|
|
7443
|
+
}
|
|
7444
|
+
/**
|
|
7445
|
+
* Get the filename for this photo
|
|
7446
|
+
*/
|
|
7447
|
+
getFilename() {
|
|
7448
|
+
return this.filename;
|
|
7449
|
+
}
|
|
7450
|
+
/**
|
|
7451
|
+
* Mark the photo as developed and notify all callbacks
|
|
7452
|
+
* This method is called by the SnapshotMessageHandler when the photo data arrives
|
|
7453
|
+
*/
|
|
7454
|
+
develop(photoData) {
|
|
7455
|
+
if (this.developed) {
|
|
7456
|
+
return;
|
|
7457
|
+
}
|
|
7458
|
+
this.photoData = photoData;
|
|
7459
|
+
this.developed = true;
|
|
7460
|
+
this.callbacks.forEach((callback) => {
|
|
7461
|
+
callback(this.photoData);
|
|
7462
|
+
});
|
|
7463
|
+
this.callbacks.clear();
|
|
7464
|
+
}
|
|
7465
|
+
}
|
|
7466
|
+
class SnapshotMessageHandler {
|
|
7467
|
+
constructor(eventBus) {
|
|
7468
|
+
this.eventBus = eventBus;
|
|
7469
|
+
logger.debug("SnapshotMessageHandler initialized");
|
|
7470
|
+
this.setupMessageListener();
|
|
7471
|
+
}
|
|
7472
|
+
pendingPhotos = /* @__PURE__ */ new Map();
|
|
7473
|
+
/**
|
|
7474
|
+
* Register a photo to be developed when its data arrives
|
|
7475
|
+
*/
|
|
7476
|
+
registerPhoto(photo) {
|
|
7477
|
+
logger.debug("Registering photo for development", { filename: photo.getFilename() });
|
|
7478
|
+
this.pendingPhotos.set(photo.getFilename(), photo);
|
|
7479
|
+
logger.debug("Pending photos count", { count: this.pendingPhotos.size });
|
|
7480
|
+
}
|
|
7481
|
+
setupMessageListener() {
|
|
7482
|
+
logger.debug("Setting up message listener for snapshot messages");
|
|
7483
|
+
this.eventBus.on(EVENT_NAMES.MESSAGE_RECEIVED, (event) => {
|
|
7484
|
+
const { message } = event;
|
|
7485
|
+
if (message.type === MESSAGE_TYPES.SNAPSHOT) {
|
|
7486
|
+
logger.debug("Processing snapshot message");
|
|
7487
|
+
this.handleSnapshotDeveloped(message);
|
|
7488
|
+
}
|
|
7489
|
+
});
|
|
7490
|
+
}
|
|
7491
|
+
handleSnapshotDeveloped(message) {
|
|
7492
|
+
logger.debug("Handling snapshot developed message", {
|
|
7493
|
+
filename: message.filename,
|
|
7494
|
+
dataLength: message.data.length,
|
|
7495
|
+
pendingCount: this.pendingPhotos.size
|
|
7496
|
+
});
|
|
7497
|
+
const photo = this.pendingPhotos.get(message.filename);
|
|
7498
|
+
if (photo !== void 0) {
|
|
7499
|
+
logger.debug("Found pending photo, developing...", { filename: message.filename });
|
|
7500
|
+
const photoData = {
|
|
7501
|
+
filename: message.filename,
|
|
7502
|
+
data: message.data
|
|
7503
|
+
};
|
|
7504
|
+
photo.develop(photoData);
|
|
7505
|
+
this.pendingPhotos.delete(message.filename);
|
|
7506
|
+
logger.debug("Photo developed and removed from pending", {
|
|
7507
|
+
filename: message.filename,
|
|
7508
|
+
remainingCount: this.pendingPhotos.size
|
|
7509
|
+
});
|
|
7510
|
+
return;
|
|
7511
|
+
}
|
|
7512
|
+
logger.warn("No pending photo found for filename", {
|
|
7513
|
+
filename: message.filename,
|
|
7514
|
+
pendingFilenames: Array.from(this.pendingPhotos.keys())
|
|
7515
|
+
});
|
|
7516
|
+
}
|
|
7517
|
+
}
|
|
7518
|
+
class Snapshot {
|
|
7519
|
+
constructor(eventBus) {
|
|
7520
|
+
this.eventBus = eventBus;
|
|
7521
|
+
this.messageHandler = new SnapshotMessageHandler(eventBus);
|
|
7522
|
+
}
|
|
7523
|
+
validExtensions = [".png", ".jpg", ".jpeg", ".webp"];
|
|
7524
|
+
messageHandler;
|
|
7525
|
+
/**
|
|
7526
|
+
* Take a snapshot of the current 3D model view
|
|
7527
|
+
*
|
|
7528
|
+
* @param filename The filename for the snapshot (must include valid extension)
|
|
7529
|
+
* @returns Photo object for registering callbacks
|
|
7530
|
+
* @throws VirtualdisplayError if filename is invalid
|
|
7531
|
+
*/
|
|
7532
|
+
take(filename) {
|
|
7533
|
+
this.validateFilename(filename);
|
|
7534
|
+
logger.debug("Taking snapshot", { filename });
|
|
7535
|
+
const photo = new Photo(filename);
|
|
7536
|
+
this.messageHandler.registerPhoto(photo);
|
|
7537
|
+
this.eventBus.emit(EVENT_NAMES.SNAPSHOT_MESSAGE, {
|
|
7538
|
+
message: {
|
|
7539
|
+
type: MESSAGE_TYPES.SNAPSHOT,
|
|
7540
|
+
filename
|
|
7541
|
+
}
|
|
7542
|
+
});
|
|
7543
|
+
return photo;
|
|
7544
|
+
}
|
|
7545
|
+
/**
|
|
7546
|
+
* Validate filename has a valid image extension
|
|
7547
|
+
*/
|
|
7548
|
+
validateFilename(filename) {
|
|
7549
|
+
if (filename === "" || typeof filename !== "string") {
|
|
7550
|
+
throw new VirtualdisplayError(
|
|
7551
|
+
"Filename must be a non-empty string",
|
|
7552
|
+
ERROR_CODES.INVALID_PARAMETER
|
|
7553
|
+
);
|
|
7554
|
+
}
|
|
7555
|
+
const hasValidExtension = this.validExtensions.some(
|
|
7556
|
+
(ext) => filename.toLowerCase().endsWith(ext)
|
|
7557
|
+
);
|
|
7558
|
+
if (!hasValidExtension) {
|
|
7559
|
+
throw new VirtualdisplayError(
|
|
7560
|
+
`Invalid filename: "${filename}". Filename must end with one of: ${this.validExtensions.join(", ")}`,
|
|
7561
|
+
ERROR_CODES.INVALID_PARAMETER
|
|
7562
|
+
);
|
|
7563
|
+
}
|
|
7564
|
+
if (filename.includes("..") || filename.includes("/") || filename.includes("\\")) {
|
|
7565
|
+
throw new VirtualdisplayError(
|
|
7566
|
+
'Filename cannot contain path separators or ".."',
|
|
7567
|
+
ERROR_CODES.INVALID_PARAMETER
|
|
7568
|
+
);
|
|
7569
|
+
}
|
|
7570
|
+
}
|
|
7571
|
+
}
|
|
7422
7572
|
class VirtualdisplayViewerService {
|
|
7423
7573
|
eventBus;
|
|
7424
7574
|
constructor(eventBus) {
|
|
@@ -7512,6 +7662,7 @@ class VirtualdisplayClient {
|
|
|
7512
7662
|
messageHandler;
|
|
7513
7663
|
viewerService;
|
|
7514
7664
|
cameraApi;
|
|
7665
|
+
snapshotApi;
|
|
7515
7666
|
options;
|
|
7516
7667
|
iframe;
|
|
7517
7668
|
constructor(options) {
|
|
@@ -7522,6 +7673,7 @@ class VirtualdisplayClient {
|
|
|
7522
7673
|
this.messageHandler = new MessageHandler(this.eventBus);
|
|
7523
7674
|
this.viewerService = new VirtualdisplayViewerService(this.eventBus);
|
|
7524
7675
|
this.cameraApi = new Camera(this.eventBus);
|
|
7676
|
+
this.snapshotApi = new Snapshot(this.eventBus);
|
|
7525
7677
|
this.initializeLogging();
|
|
7526
7678
|
this.createAndConnectIframe();
|
|
7527
7679
|
this.setupInitialConfig();
|
|
@@ -7621,6 +7773,12 @@ class VirtualdisplayClient {
|
|
|
7621
7773
|
get camera() {
|
|
7622
7774
|
return this.cameraApi;
|
|
7623
7775
|
}
|
|
7776
|
+
/**
|
|
7777
|
+
* Get the snapshot API for taking screenshots
|
|
7778
|
+
*/
|
|
7779
|
+
get snapshot() {
|
|
7780
|
+
return this.snapshotApi;
|
|
7781
|
+
}
|
|
7624
7782
|
destroy() {
|
|
7625
7783
|
logger.info("Destroying Virtualdisplay client");
|
|
7626
7784
|
this.attributeService.clear();
|
|
@@ -7645,6 +7803,8 @@ export {
|
|
|
7645
7803
|
Mutation,
|
|
7646
7804
|
NODE_TYPES,
|
|
7647
7805
|
NodeSelector,
|
|
7806
|
+
Photo,
|
|
7807
|
+
Snapshot,
|
|
7648
7808
|
VirtualdisplayClient,
|
|
7649
7809
|
VirtualdisplayError,
|
|
7650
7810
|
VirtualdisplayViewerService,
|