rnxsim 0.1.514 → 0.1.516

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 (60) hide show
  1. package/cli/cloud-client.ts +18 -8
  2. package/cli/commands/inspect/get-layout.ts +2 -1
  3. package/cli/outbound-endpoints.ts +1 -1
  4. package/dist-lib/agent-daemon-client.cjs +1 -1
  5. package/dist-lib/agent-events.cjs +1 -1
  6. package/dist-lib/agent-identity.cjs +1 -1
  7. package/dist-lib/agent-sessions.cjs +1 -1
  8. package/dist-lib/attached-projects.cjs +1 -1
  9. package/dist-lib/auth/shared-session.cjs +1 -1
  10. package/dist-lib/backend-origin.cjs +1 -1
  11. package/dist-lib/beta.cjs +1 -1
  12. package/dist-lib/beta.mjs +1 -1
  13. package/dist-lib/bridge-constants.cjs +1 -1
  14. package/dist-lib/bridge-contract-input.cjs +64 -1
  15. package/dist-lib/bridge-contract-input.mjs +63 -1
  16. package/dist-lib/bridge-contract.cjs +73 -3
  17. package/dist-lib/bridge-contract.mjs +69 -2
  18. package/dist-lib/capture-contract.cjs +1 -1
  19. package/dist-lib/capture-contract.mjs +1 -1
  20. package/dist-lib/cli-constants.cjs +1 -1
  21. package/dist-lib/cloud-contract.cjs +63 -22
  22. package/dist-lib/cloud-contract.mjs +57 -21
  23. package/dist-lib/cloud.cjs +56 -20
  24. package/dist-lib/cloud.mjs +56 -20
  25. package/dist-lib/config.cjs +1 -1
  26. package/dist-lib/detox/index.cjs +1 -1
  27. package/dist-lib/dev-bundle-resolution.cjs +1 -1
  28. package/dist-lib/home-paths.cjs +1 -1
  29. package/dist-lib/host/bridge-host.cjs +45 -20
  30. package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
  31. package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
  32. package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
  33. package/dist-lib/host/replacement-module-handler.cjs +1 -1
  34. package/dist-lib/host/websocket-proxy.cjs +1 -1
  35. package/dist-lib/index.cjs +62 -24
  36. package/dist-lib/jump-to-source-babel.cjs +1 -1
  37. package/dist-lib/jump-to-source-native.cjs +1 -1
  38. package/dist-lib/menu.cjs +1 -1
  39. package/dist-lib/menu.mjs +1 -1
  40. package/dist-lib/metro-fingerprint-registry.cjs +1 -1
  41. package/dist-lib/metro-fingerprint-registry.mjs +1 -1
  42. package/dist-lib/metro-production-bundle.cjs +1 -1
  43. package/dist-lib/metro-production-bundle.mjs +1 -1
  44. package/dist-lib/metro.cjs +1 -1
  45. package/dist-lib/profiles.cjs +1 -1
  46. package/dist-lib/public-brand.cjs +1 -1
  47. package/dist-lib/react-native-host-modules.cjs +1 -1
  48. package/dist-lib/react-native-host-modules.mjs +1 -1
  49. package/dist-lib/render-mode.cjs +1 -1
  50. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  51. package/dist-lib/sdk.cjs +1 -1
  52. package/dist-lib/sdk.mjs +1 -1
  53. package/dist-lib/skills.cjs +62 -24
  54. package/dist-lib/swift.cjs +1 -1
  55. package/dist-lib/vite.cjs +1 -1
  56. package/package.json +2 -1
  57. package/src/bridge-contract-input.ts +74 -0
  58. package/src/bridge-contract.ts +103 -0
  59. package/src/cloud-contract.ts +71 -19
  60. package/src/cloud.ts +10 -0
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -78,29 +78,60 @@ function isLoopbackHost(hostname) {
78
78
  // src/cloud-contract.ts
79
79
  var RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
80
80
  var RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
81
- var RNX_CLOUD_COMMAND_TYPES = Object.freeze([
82
- "state",
83
- "query",
84
- "resolve",
85
- "perform",
86
- "waitFor",
87
- "reset",
88
- "screenshot",
89
- "capture",
90
- "captureRegions",
91
- "settle",
92
- "setAppearance",
93
- "openUrl",
94
- "tree",
95
- "tap",
96
- "longPress",
97
- "memory",
98
- "diagnostics"
99
- ]);
81
+ var RNX_CLOUD_COMMAND_SCOPES = {
82
+ state: "read",
83
+ tree: "read",
84
+ query: "read",
85
+ resolve: "read",
86
+ screenshot: "read",
87
+ capture: "read",
88
+ captureRegions: "read",
89
+ memory: "read",
90
+ diagnostics: "read",
91
+ storageSnapshot: "read",
92
+ perform: "drive",
93
+ waitFor: "drive",
94
+ tap: "drive",
95
+ longPress: "drive",
96
+ settle: "drive",
97
+ openUrl: "drive",
98
+ setAppearance: "drive",
99
+ keyboard: "drive",
100
+ focus: "drive",
101
+ flowStatus: "drive",
102
+ reset: "reset",
103
+ camera: "media",
104
+ // string evaluation and arbitrary bridge calls are maestro's boundary: the
105
+ // customer sdk never carries this scope, and the service grants it only to
106
+ // the account api key caller. close rides along, since ending the page ends
107
+ // the sim and belongs to the sim's owner rather than any driver.
108
+ evaluate: "debug",
109
+ call: "debug",
110
+ close: "debug"
111
+ };
112
+ function scopeTableKeys() {
113
+ return Object.keys(RNX_CLOUD_COMMAND_SCOPES).filter(
114
+ (key) => Object.prototype.hasOwnProperty.call(RNX_CLOUD_COMMAND_SCOPES, key)
115
+ );
116
+ }
117
+ var RNX_CLOUD_COMMAND_TYPES = Object.freeze(scopeTableKeys());
100
118
  var RNX_CLOUD_COMMAND_TYPE_SET = new Set(RNX_CLOUD_COMMAND_TYPES);
101
119
  function isRnxCloudCommandType(value) {
102
120
  return RNX_CLOUD_COMMAND_TYPE_SET.has(value);
103
121
  }
122
+ function rnxCloudCommandScope(type) {
123
+ return RNX_CLOUD_COMMAND_SCOPES[type];
124
+ }
125
+ var RNX_CLOUD_TOKEN_SCOPES = Object.freeze([
126
+ "read",
127
+ "drive",
128
+ "reset",
129
+ "media"
130
+ ]);
131
+ var RNX_CLOUD_ALL_COMMAND_SCOPES = Object.freeze(["read", "drive", "reset", "media", "debug"]);
132
+ function rnxCloudScopesAllowCommand(scopes, type) {
133
+ return scopes.includes(RNX_CLOUD_COMMAND_SCOPES[type]);
134
+ }
104
135
 
105
136
  // src/metro-production-bundle.ts
106
137
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -444,10 +475,17 @@ function cloudCommand(command) {
444
475
  for (const [key, value] of Object.entries(command)) {
445
476
  if (key !== "id" && key !== "simId") clean[key] = value;
446
477
  }
447
- if (isRnxCloudCommandType(command.type)) return clean;
448
- throw new Error(
449
- `a remote simulator does not support bridge command type ${command.type}`
450
- );
478
+ if (!isRnxCloudCommandType(command.type)) {
479
+ throw new Error(
480
+ `a remote simulator does not support bridge command type ${command.type}`
481
+ );
482
+ }
483
+ if (!rnxCloudScopesAllowCommand(RNX_CLOUD_TOKEN_SCOPES, command.type)) {
484
+ throw new Error(
485
+ `a remote simulator command ${command.type} requires the ${rnxCloudCommandScope(command.type)} scope`
486
+ );
487
+ }
488
+ return clean;
451
489
  }
452
490
  var CloudBridge = class {
453
491
  constructor(session) {
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/menu.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/menu.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/public-brand.ts
4
4
  var name = "rnx";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-fingerprint-registry.ts
4
4
  var RNX_METRO_FINGERPRINT_SCHEMA_VERSION = 2;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/metro-production-bundle.ts
4
4
  var RNXSIM_METRO_MODULE_PATHS_PREFIX = "globalThis.__sootsimModulePaths=";
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
 
3
3
  // src/react-native-host-modules.ts
4
4
  var REACT_NATIVE_PASSTHROUGH_SPECIFIERS = [
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/dist-lib/sdk.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/sdk.mjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
@@ -27513,35 +27513,66 @@ var init_metro_fingerprint = __esm({
27513
27513
  });
27514
27514
 
27515
27515
  // src/cloud-contract.ts
27516
+ function scopeTableKeys() {
27517
+ return Object.keys(RNX_CLOUD_COMMAND_SCOPES).filter(
27518
+ (key) => Object.prototype.hasOwnProperty.call(RNX_CLOUD_COMMAND_SCOPES, key)
27519
+ );
27520
+ }
27516
27521
  function isRnxCloudCommandType(value) {
27517
27522
  return RNX_CLOUD_COMMAND_TYPE_SET.has(value);
27518
27523
  }
27519
- var RNX_CLOUD_MAX_ARTIFACT_BYTES, RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES, RNX_CLOUD_COMMAND_TYPES, RNX_CLOUD_COMMAND_TYPE_SET;
27524
+ function rnxCloudCommandScope(type) {
27525
+ return RNX_CLOUD_COMMAND_SCOPES[type];
27526
+ }
27527
+ function rnxCloudScopesAllowCommand(scopes, type) {
27528
+ return scopes.includes(RNX_CLOUD_COMMAND_SCOPES[type]);
27529
+ }
27530
+ var RNX_CLOUD_MAX_ARTIFACT_BYTES, RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES, RNX_CLOUD_COMMAND_SCOPES, RNX_CLOUD_COMMAND_TYPES, RNX_CLOUD_COMMAND_TYPE_SET, RNX_CLOUD_TOKEN_SCOPES, RNX_CLOUD_ALL_COMMAND_SCOPES;
27520
27531
  var init_cloud_contract = __esm({
27521
27532
  "src/cloud-contract.ts"() {
27522
27533
  "use strict";
27523
27534
  RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024;
27524
27535
  RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024;
27525
- RNX_CLOUD_COMMAND_TYPES = Object.freeze([
27526
- "state",
27527
- "query",
27528
- "resolve",
27529
- "perform",
27530
- "waitFor",
27536
+ RNX_CLOUD_COMMAND_SCOPES = {
27537
+ state: "read",
27538
+ tree: "read",
27539
+ query: "read",
27540
+ resolve: "read",
27541
+ screenshot: "read",
27542
+ capture: "read",
27543
+ captureRegions: "read",
27544
+ memory: "read",
27545
+ diagnostics: "read",
27546
+ storageSnapshot: "read",
27547
+ perform: "drive",
27548
+ waitFor: "drive",
27549
+ tap: "drive",
27550
+ longPress: "drive",
27551
+ settle: "drive",
27552
+ openUrl: "drive",
27553
+ setAppearance: "drive",
27554
+ keyboard: "drive",
27555
+ focus: "drive",
27556
+ flowStatus: "drive",
27557
+ reset: "reset",
27558
+ camera: "media",
27559
+ // string evaluation and arbitrary bridge calls are maestro's boundary: the
27560
+ // customer sdk never carries this scope, and the service grants it only to
27561
+ // the account api key caller. close rides along, since ending the page ends
27562
+ // the sim and belongs to the sim's owner rather than any driver.
27563
+ evaluate: "debug",
27564
+ call: "debug",
27565
+ close: "debug"
27566
+ };
27567
+ RNX_CLOUD_COMMAND_TYPES = Object.freeze(scopeTableKeys());
27568
+ RNX_CLOUD_COMMAND_TYPE_SET = new Set(RNX_CLOUD_COMMAND_TYPES);
27569
+ RNX_CLOUD_TOKEN_SCOPES = Object.freeze([
27570
+ "read",
27571
+ "drive",
27531
27572
  "reset",
27532
- "screenshot",
27533
- "capture",
27534
- "captureRegions",
27535
- "settle",
27536
- "setAppearance",
27537
- "openUrl",
27538
- "tree",
27539
- "tap",
27540
- "longPress",
27541
- "memory",
27542
- "diagnostics"
27573
+ "media"
27543
27574
  ]);
27544
- RNX_CLOUD_COMMAND_TYPE_SET = new Set(RNX_CLOUD_COMMAND_TYPES);
27575
+ RNX_CLOUD_ALL_COMMAND_SCOPES = Object.freeze(["read", "drive", "reset", "media", "debug"]);
27545
27576
  }
27546
27577
  });
27547
27578
 
@@ -28270,10 +28301,17 @@ function cloudCommand(command) {
28270
28301
  for (const [key, value] of Object.entries(command)) {
28271
28302
  if (key !== "id" && key !== "simId") clean[key] = value;
28272
28303
  }
28273
- if (isRnxCloudCommandType(command.type)) return clean;
28274
- throw new Error(
28275
- `a remote simulator does not support bridge command type ${command.type}`
28276
- );
28304
+ if (!isRnxCloudCommandType(command.type)) {
28305
+ throw new Error(
28306
+ `a remote simulator does not support bridge command type ${command.type}`
28307
+ );
28308
+ }
28309
+ if (!rnxCloudScopesAllowCommand(RNX_CLOUD_TOKEN_SCOPES, command.type)) {
28310
+ throw new Error(
28311
+ `a remote simulator command ${command.type} requires the ${rnxCloudCommandScope(command.type)} scope`
28312
+ );
28313
+ }
28314
+ return clean;
28277
28315
  }
28278
28316
  function createCloudBridgeForParsed(parsed) {
28279
28317
  const session = resolveCloudSessionForParsed(parsed);
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __defProp = Object.defineProperty;
package/dist-lib/vite.cjs CHANGED
@@ -1,4 +1,4 @@
1
- /*! rnx v0.1.514 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
1
+ /*! rnx v0.1.516 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
2
2
  let __sootsim_import_meta_url = ''; try { __sootsim_import_meta_url = require('url').pathToFileURL(__filename).href; } catch {}
3
3
  "use strict";
4
4
  var __create = Object.create;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rnxsim",
3
- "version": "0.1.514",
3
+ "version": "0.1.516",
4
4
  "description": "Vite and Metro plugins, testing drivers, and SDK exports for rnx.",
5
5
  "author": "Tamagui LLC",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -92,6 +92,7 @@
92
92
  "./bridge-contract-input": {
93
93
  "types": "./src/bridge-contract-input.ts",
94
94
  "source": "./src/bridge-contract-input.ts",
95
+ "bun": "./src/bridge-contract-input.ts",
95
96
  "import": "./dist-lib/bridge-contract-input.mjs",
96
97
  "default": "./dist-lib/bridge-contract-input.cjs"
97
98
  },
@@ -198,6 +198,46 @@ export function parsePerformStep(value: unknown, index: number): SimPerformStep
198
198
  return { type, key: stringValue(step.key, `${path}.key`) }
199
199
  case 'dismissKeyboard':
200
200
  return { type }
201
+ case 'setElementValue': {
202
+ const submit = optionalBoolean(step, 'submit', path)
203
+ return {
204
+ type,
205
+ id: stringValue(step.id, `${path}.id`),
206
+ text: stringValue(step.text, `${path}.text`),
207
+ ...(submit === undefined ? {} : { submit }),
208
+ }
209
+ }
210
+ case 'toggleKeyboard':
211
+ return { type }
212
+ case 'openUrl': {
213
+ const url = stringValue(step.url, `${path}.url`)
214
+ if (url.length === 0) validationFailure(`${path}.url`, 'must be non-empty')
215
+ return { type, url }
216
+ }
217
+ case 'keyDown':
218
+ case 'keyUp':
219
+ return { type, key: stringValue(step.key, `${path}.key`) }
220
+ case 'setOrientation': {
221
+ const orientation = stringValue(step.orientation, `${path}.orientation`)
222
+ if (orientation !== 'portrait' && orientation !== 'landscape') {
223
+ validationFailure(`${path}.orientation`, 'must be portrait or landscape')
224
+ }
225
+ return { type, orientation }
226
+ }
227
+ case 'deviceInfo':
228
+ return { type }
229
+ case 'buttonDown':
230
+ case 'buttonUp': {
231
+ const button =
232
+ step.button === undefined ? undefined : stringValue(step.button, `${path}.button`)
233
+ return { type, ...(button === undefined ? {} : { button }) }
234
+ }
235
+ case 'incrementElement':
236
+ case 'decrementElement':
237
+ return { type, id: stringValue(step.id, `${path}.id`) }
238
+ case 'discoverStoreKitConfig':
239
+ case 'clearStoreKitConfig':
240
+ return { type }
201
241
  case 'wait':
202
242
  return { type, ms: numberValue(step.ms, `${path}.ms`) }
203
243
  case 'waitFor': {
@@ -233,6 +273,40 @@ export function parsePerformStep(value: unknown, index: number): SimPerformStep
233
273
  }
234
274
  }
235
275
 
276
+ // an unclosed touchDown leaves a pointer stuck down, and the batch abort path
277
+ // would cancel it after the caller was told the batch failed. every batch is
278
+ // self-contained (pointers never survive it), so a touchDown without its
279
+ // touchUp or touchCancel in the same batch is rejected before anything runs.
280
+ export function findUnbalancedTouchDown(
281
+ steps: readonly SimPerformStep[],
282
+ ): { index: number; pointerId: number } | null {
283
+ const open = new Map<number, number>()
284
+ const pointerIdOf = (step: SimPerformStep): number | null => {
285
+ if (
286
+ step.type !== 'touchDown' &&
287
+ step.type !== 'touchMove' &&
288
+ step.type !== 'touchUp' &&
289
+ step.type !== 'touchCancel'
290
+ ) {
291
+ return null
292
+ }
293
+ const pointerId = step.pointerId ?? 999
294
+ return Number.isSafeInteger(pointerId) ? pointerId : null
295
+ }
296
+ for (const [index, step] of steps.entries()) {
297
+ if (step.type === 'touchDown') {
298
+ const pointerId = pointerIdOf(step) ?? 999
299
+ if (!open.has(pointerId)) open.set(pointerId, index)
300
+ } else if (step.type === 'touchUp' || step.type === 'touchCancel') {
301
+ const pointerId = pointerIdOf(step)
302
+ if (pointerId !== null) open.delete(pointerId)
303
+ }
304
+ }
305
+ const next = open.entries().next()
306
+ if (next.done) return null
307
+ return { index: next.value[1], pointerId: next.value[0] }
308
+ }
309
+
236
310
  export function parseStateOptions(value: unknown): SimStateOptions {
237
311
  const input = recordValue(value, 'state')
238
312
  const route = optionalBoolean(input, 'route', 'state')
@@ -163,6 +163,24 @@ export type PerformStep =
163
163
  condition?: 'present' | 'mounted' | 'absent'
164
164
  timeoutMs?: number
165
165
  }
166
+ // limrun parity steps. each maps onto an engine primitive and reports
167
+ // per step; the refused ones below parse but never run.
168
+ | { type: 'setElementValue'; id: string; text: string; submit?: boolean }
169
+ | { type: 'toggleKeyboard' }
170
+ | { type: 'openUrl'; url: string }
171
+ | { type: 'keyDown'; key: string }
172
+ | { type: 'keyUp'; key: string }
173
+ | { type: 'setOrientation'; orientation: 'portrait' | 'landscape' }
174
+ | { type: 'deviceInfo' }
175
+ // refused with a named reason, never emulated: no hardware-button model,
176
+ // no storekit simulation, no stepper primitive, and key/orientation state
177
+ // the engine does not hold.
178
+ | { type: 'buttonDown'; button?: string }
179
+ | { type: 'buttonUp'; button?: string }
180
+ | { type: 'incrementElement'; id: string }
181
+ | { type: 'decrementElement'; id: string }
182
+ | { type: 'discoverStoreKitConfig' }
183
+ | { type: 'clearStoreKitConfig' }
166
184
 
167
185
  export interface PerformOptions {
168
186
  // stop the batch at the first failing step. default true — a gesture whose
@@ -179,6 +197,91 @@ export interface PerformOptions {
179
197
  timeoutMs?: number
180
198
  }
181
199
 
200
+ // observed device identity for the `deviceInfo` step: the live device spec
201
+ // in both runtimes, so every field is measured, never a default.
202
+ export interface PerformDeviceInfo {
203
+ platform: string
204
+ model?: string
205
+ width: number
206
+ height: number
207
+ scale: number
208
+ }
209
+
210
+ // steps that parse but never run. one owner for the refusal vocabulary so
211
+ // the browser page and the cpu runtime refuse with the same named reason and
212
+ // neither plane emulates what the engine cannot do.
213
+ export const REFUSED_PERFORM_STEP_TYPES = [
214
+ 'keyDown',
215
+ 'keyUp',
216
+ 'setOrientation',
217
+ 'buttonDown',
218
+ 'buttonUp',
219
+ 'incrementElement',
220
+ 'decrementElement',
221
+ 'discoverStoreKitConfig',
222
+ 'clearStoreKitConfig',
223
+ ] as const
224
+
225
+ export type RefusedPerformStepType = (typeof REFUSED_PERFORM_STEP_TYPES)[number]
226
+
227
+ export function refusedPerformStepReason(type: RefusedPerformStepType): string {
228
+ switch (type) {
229
+ case 'keyDown':
230
+ case 'keyUp':
231
+ return `${type} is not supported: keys dispatch atomically; use the key step`
232
+ case 'setOrientation':
233
+ return 'setOrientation is not supported: the simulator viewport is fixed at boot'
234
+ case 'buttonDown':
235
+ case 'buttonUp':
236
+ return `${type} is not supported: the simulator has no hardware buttons`
237
+ case 'incrementElement':
238
+ case 'decrementElement':
239
+ return `${type} is not supported: steppers have no engine primitive; drive them with tapId`
240
+ case 'discoverStoreKitConfig':
241
+ case 'clearStoreKitConfig':
242
+ return `${type} is not supported: storekit has no simulator model`
243
+ }
244
+ }
245
+
246
+ // returns the refusal for a refused step, null when the step is runnable.
247
+ // every variant is listed so adding a step forces its classification here;
248
+ // the executors mirror the refused cases in their own switches.
249
+ export function performStepRefusal(type: PerformStep['type']): string | null {
250
+ switch (type) {
251
+ case 'keyDown':
252
+ case 'keyUp':
253
+ case 'setOrientation':
254
+ case 'buttonDown':
255
+ case 'buttonUp':
256
+ case 'incrementElement':
257
+ case 'decrementElement':
258
+ case 'discoverStoreKitConfig':
259
+ case 'clearStoreKitConfig':
260
+ return refusedPerformStepReason(type)
261
+ case 'touchDown':
262
+ case 'touchMove':
263
+ case 'touchUp':
264
+ case 'touchCancel':
265
+ case 'tap':
266
+ case 'tapId':
267
+ case 'doubleTap':
268
+ case 'longPress':
269
+ case 'drag':
270
+ case 'pinch':
271
+ case 'scroll':
272
+ case 'type':
273
+ case 'key':
274
+ case 'dismissKeyboard':
275
+ case 'setElementValue':
276
+ case 'toggleKeyboard':
277
+ case 'openUrl':
278
+ case 'deviceInfo':
279
+ case 'wait':
280
+ case 'waitFor':
281
+ return null
282
+ }
283
+ }
284
+
182
285
  export interface PerformStepResult {
183
286
  index: number
184
287
  type: PerformStep['type']