@helix3/helix-cli 0.1.18-helix3.340 → 0.1.18-helix3.342
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/dist/device.d.ts +1 -1
- package/dist/device.js +84 -0
- package/dist/device.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/visualQa/command.js +3 -3
- package/dist/visualQa/command.js.map +1 -1
- package/dist/visualQa/resolve.d.ts +2 -1
- package/dist/visualQa/resolve.js +18 -1
- package/dist/visualQa/resolve.js.map +1 -1
- package/package.json +2 -2
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_abs.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_expression.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_jbeam.cpython-312.pyc +0 -0
- package/plugins/beamng-vehicle/bin/__pycache__/beamng_materials.cpython-312.pyc +0 -0
package/dist/device.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export declare const DEVICE_INTERACTION_VERB_LIST: string;
|
|
|
23
23
|
* fail `helix device validate` while the client loaded it happily. Add
|
|
24
24
|
* capabilities to both lists, never to one.
|
|
25
25
|
*/
|
|
26
|
-
export declare const DEVICE_CAPABILITIES: readonly ["model.glb", "presentation.nodeMotion", "clock.serverTime", "state.typed", "state.durable", "room.authority", "audio.playback", "presentation.light", "presentation.vfx", "character.posture", "character.animate", "character.hand", "signals.local", "screen.render", "screen.input", "app.web.local", "app.web.remote", "app.image.remote", "app.image.owner", "app.canvas", "device.ports", "physics.solid"];
|
|
26
|
+
export declare const DEVICE_CAPABILITIES: readonly ["model.glb", "presentation.nodeMotion", "clock.serverTime", "state.typed", "state.durable", "room.authority", "audio.playback", "presentation.light", "presentation.vfx", "character.posture", "character.animate", "character.hand", "signals.local", "screen.render", "screen.input", "app.web.local", "app.web.remote", "app.web.panel", "app.image.remote", "app.image.owner", "app.canvas", "device.ports", "physics.solid"];
|
|
27
27
|
/**
|
|
28
28
|
* The closed component vocabulary.
|
|
29
29
|
*
|
package/dist/device.js
CHANGED
|
@@ -75,6 +75,7 @@ exports.DEVICE_CAPABILITIES = [
|
|
|
75
75
|
'screen.input',
|
|
76
76
|
'app.web.local',
|
|
77
77
|
'app.web.remote',
|
|
78
|
+
'app.web.panel',
|
|
78
79
|
'app.image.remote',
|
|
79
80
|
'app.image.owner',
|
|
80
81
|
'app.canvas',
|
|
@@ -1232,6 +1233,80 @@ function exerciseValue(interaction, state) {
|
|
|
1232
1233
|
}
|
|
1233
1234
|
return undefined;
|
|
1234
1235
|
}
|
|
1236
|
+
/** Viewer-local declarations never become room actions or state writes. */
|
|
1237
|
+
function deviceWebPanelInteractionError(definition, interaction) {
|
|
1238
|
+
const plainRecord = (value) => isRecord(value) ? value : null;
|
|
1239
|
+
if (interaction.presentation === undefined)
|
|
1240
|
+
return null;
|
|
1241
|
+
const presentation = plainRecord(interaction.presentation);
|
|
1242
|
+
if (!presentation ||
|
|
1243
|
+
presentation.kind !== 'webPanel' ||
|
|
1244
|
+
Object.keys(presentation).some((key) => key !== 'kind' && key !== 'app') ||
|
|
1245
|
+
typeof presentation.app !== 'string' ||
|
|
1246
|
+
!presentation.app.trim() ||
|
|
1247
|
+
presentation.app.length > 64)
|
|
1248
|
+
return 'presentation must be {kind:webPanel,app:<declared app id>}';
|
|
1249
|
+
if (interaction.action !== undefined || interaction.control !== undefined)
|
|
1250
|
+
return 'webPanel presentation cannot include action or control';
|
|
1251
|
+
if (interaction.surface === 'internal')
|
|
1252
|
+
return 'webPanel presentation cannot use an internal interaction';
|
|
1253
|
+
const capabilities = definition.capabilities;
|
|
1254
|
+
if (!Array.isArray(capabilities) || !capabilities.includes('app.web.panel'))
|
|
1255
|
+
return 'webPanel presentation requires capability app.web.panel';
|
|
1256
|
+
const apps = plainRecord(definition.apps);
|
|
1257
|
+
const app = apps && Object.prototype.hasOwnProperty.call(apps, presentation.app)
|
|
1258
|
+
? plainRecord(apps[presentation.app])
|
|
1259
|
+
: null;
|
|
1260
|
+
if (!app || (app.type !== 'remoteWeb' && app.type !== 'localWeb'))
|
|
1261
|
+
return 'webPanel presentation must target a declared remoteWeb or localWeb app';
|
|
1262
|
+
if (!capabilities.includes(app.type === 'remoteWeb' ? 'app.web.remote' : 'app.web.local'))
|
|
1263
|
+
return `webPanel app requires capability ${app.type === 'remoteWeb' ? 'app.web.remote' : 'app.web.local'}`;
|
|
1264
|
+
if (typeof app.entry !== 'string' ||
|
|
1265
|
+
!app.entry.trim() ||
|
|
1266
|
+
app.entry.length > 2048)
|
|
1267
|
+
return 'webPanel app entry must be a non-empty string of at most 2048 characters';
|
|
1268
|
+
if (app.type === 'localWeb') {
|
|
1269
|
+
if (app.allowedOrigins !== undefined)
|
|
1270
|
+
return 'webPanel localWeb app cannot declare allowedOrigins';
|
|
1271
|
+
try {
|
|
1272
|
+
const url = new URL(app.entry, 'https://helix.invalid');
|
|
1273
|
+
if (!app.entry.startsWith('/device-apps/') ||
|
|
1274
|
+
app.entry.includes('\\') ||
|
|
1275
|
+
app.entry.split(/[/?#]/).includes('..') ||
|
|
1276
|
+
url.origin !== 'https://helix.invalid' ||
|
|
1277
|
+
!/^\/device-apps\/[A-Za-z0-9][A-Za-z0-9/_.-]*$/.test(url.pathname) ||
|
|
1278
|
+
decodeURIComponent(url.pathname) !== url.pathname)
|
|
1279
|
+
return 'webPanel localWeb entry must use a safe /device-apps/ path';
|
|
1280
|
+
}
|
|
1281
|
+
catch {
|
|
1282
|
+
return 'webPanel localWeb entry must use a safe /device-apps/ path';
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
else {
|
|
1286
|
+
try {
|
|
1287
|
+
const url = new URL(app.entry);
|
|
1288
|
+
const origins = app.allowedOrigins;
|
|
1289
|
+
if (url.protocol !== 'https:' ||
|
|
1290
|
+
url.username ||
|
|
1291
|
+
url.password ||
|
|
1292
|
+
!Array.isArray(origins) ||
|
|
1293
|
+
origins.length < 1 ||
|
|
1294
|
+
origins.length > 16 ||
|
|
1295
|
+
!origins.every((origin) => {
|
|
1296
|
+
if (typeof origin !== 'string')
|
|
1297
|
+
return false;
|
|
1298
|
+
const allowed = new URL(origin);
|
|
1299
|
+
return allowed.protocol === 'https:' && allowed.origin === origin;
|
|
1300
|
+
}) ||
|
|
1301
|
+
!origins.includes(url.origin))
|
|
1302
|
+
return 'webPanel remoteWeb entry must use HTTPS and a declared allowedOrigins origin';
|
|
1303
|
+
}
|
|
1304
|
+
catch {
|
|
1305
|
+
return 'webPanel remoteWeb entry must use HTTPS and a declared allowedOrigins origin';
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
return null;
|
|
1309
|
+
}
|
|
1235
1310
|
function validateDeviceProject(input) {
|
|
1236
1311
|
const target = readDefinitionPath(input);
|
|
1237
1312
|
const errors = [];
|
|
@@ -1444,6 +1519,9 @@ function validateDeviceProject(input) {
|
|
|
1444
1519
|
errors.push(`interactions: ${interactions.length} exceeds budget ${DEVICE_BUDGETS.maxInteractions}`);
|
|
1445
1520
|
const interactionIds = new Set();
|
|
1446
1521
|
for (const interaction of interactions) {
|
|
1522
|
+
const presentationError = deviceWebPanelInteractionError(definition, interaction);
|
|
1523
|
+
if (presentationError)
|
|
1524
|
+
errors.push(`interaction ${String(interaction.id)}: ${presentationError}`);
|
|
1447
1525
|
const id = typeof interaction.id === 'string' ? interaction.id : '';
|
|
1448
1526
|
if (!id)
|
|
1449
1527
|
errors.push('interaction id must be a non-empty string');
|
|
@@ -2177,6 +2255,8 @@ function validateDeviceProject(input) {
|
|
|
2177
2255
|
return;
|
|
2178
2256
|
}
|
|
2179
2257
|
if (on === 'interact') {
|
|
2258
|
+
if (interactionsById.get(String(trigger.interaction))?.presentation !== undefined)
|
|
2259
|
+
errors.push(`${triggerPath}: webPanel presentation cannot be a graph interaction trigger`);
|
|
2180
2260
|
if (!interactionIds.has(String(trigger.interaction))) {
|
|
2181
2261
|
errors.push(`graph references undeclared interaction "${String(trigger.interaction)}"`);
|
|
2182
2262
|
}
|
|
@@ -2308,6 +2388,10 @@ function validateDeviceProject(input) {
|
|
|
2308
2388
|
'app.web.remote',
|
|
2309
2389
|
Object.values(apps).some((raw) => ['remoteWeb', 'youtube'].includes(String(object(raw).type))),
|
|
2310
2390
|
],
|
|
2391
|
+
[
|
|
2392
|
+
'app.web.panel',
|
|
2393
|
+
interactions.some((interaction) => interaction.presentation !== undefined),
|
|
2394
|
+
],
|
|
2311
2395
|
['device.ports', ports.length > 0],
|
|
2312
2396
|
];
|
|
2313
2397
|
for (const [capability, used] of requiresByContent) {
|