rnxsim 0.1.393 → 0.1.394
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/cli/cloud-client.ts +74 -52
- package/cli/cloud-dispatch.ts +2 -1
- package/cli/commands/inspect/core.ts +33 -2
- package/cli/commands/platform.ts +34 -9
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.cjs +1 -1
- package/dist-lib/agent-identity.cjs +1 -1
- package/dist-lib/agent-sessions.cjs +1 -1
- package/dist-lib/attached-projects.cjs +1 -1
- package/dist-lib/auth/shared-session.cjs +1 -1
- package/dist-lib/backend-origin.cjs +1 -1
- package/dist-lib/beta.cjs +1 -1
- package/dist-lib/beta.mjs +1 -1
- package/dist-lib/bridge-constants.cjs +1 -1
- package/dist-lib/bridge-contract-input.cjs +12 -1
- package/dist-lib/bridge-contract-input.mjs +10 -1
- package/dist-lib/bridge-contract.cjs +7 -1
- package/dist-lib/bridge-contract.mjs +5 -1
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/cloud-contract.cjs +29 -3
- package/dist-lib/cloud-contract.mjs +26 -2
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +1 -1
- package/dist-lib/host/fetch-proxy-handler.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.cjs +1 -1
- package/dist-lib/host/fetch-proxy-overrides.mjs +1 -1
- package/dist-lib/host/replacement-module-handler.cjs +1 -1
- package/dist-lib/host/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +23 -3
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +132 -3
- package/dist-lib/metro-production-bundle.mjs +129 -2
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +24 -3
- package/dist-lib/sdk.mjs +24 -3
- package/dist-lib/skills.cjs +28 -25
- package/dist-lib/vite.cjs +1 -1
- package/package.json +1 -1
- package/src/bridge-contract-input.ts +12 -0
- package/src/bridge-contract.ts +7 -0
- package/src/cloud-contract.ts +29 -0
- package/src/metro-production-bundle.ts +200 -0
package/dist-lib/vite.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! rnx v0.1.
|
|
1
|
+
/*! rnx v0.1.394 | (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
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// WebSocket routes) validates its request bodies here rather than growing a
|
|
4
4
|
// second reader for the same shapes.
|
|
5
5
|
|
|
6
|
+
import { SIM_LONG_PRESS_MAX_MS } from './bridge-contract'
|
|
6
7
|
import type {
|
|
7
8
|
ResetOptions,
|
|
8
9
|
SemanticWaitOptions,
|
|
@@ -130,6 +131,17 @@ export function parsePerformStep(value: unknown, index: number): SimPerformStep
|
|
|
130
131
|
}
|
|
131
132
|
case 'longPress': {
|
|
132
133
|
const durationMs = optionalNumber(step, 'durationMs', path)
|
|
134
|
+
if (
|
|
135
|
+
durationMs !== undefined &&
|
|
136
|
+
(!Number.isInteger(durationMs) ||
|
|
137
|
+
durationMs < 1 ||
|
|
138
|
+
durationMs > SIM_LONG_PRESS_MAX_MS)
|
|
139
|
+
) {
|
|
140
|
+
validationFailure(
|
|
141
|
+
`${path}.durationMs`,
|
|
142
|
+
`must be an integer between 1 and ${SIM_LONG_PRESS_MAX_MS}`,
|
|
143
|
+
)
|
|
144
|
+
}
|
|
133
145
|
return {
|
|
134
146
|
type,
|
|
135
147
|
x: numberValue(step.x, `${path}.x`),
|
package/src/bridge-contract.ts
CHANGED
|
@@ -22,6 +22,7 @@ export type BridgeCommandType =
|
|
|
22
22
|
| 'settle'
|
|
23
23
|
| 'setAppearance'
|
|
24
24
|
| 'openUrl'
|
|
25
|
+
| 'memory'
|
|
25
26
|
| 'tap'
|
|
26
27
|
| 'longPress'
|
|
27
28
|
| 'keyboard'
|
|
@@ -216,6 +217,12 @@ export interface SemanticReadyProbe {
|
|
|
216
217
|
suppressedEntryError: string
|
|
217
218
|
}
|
|
218
219
|
|
|
220
|
+
// react native's own Pressable/Touchable delayLongPress default, and the
|
|
221
|
+
// ceiling any transport accepts for a press-and-hold. a driver that holds
|
|
222
|
+
// longer than this is describing a drag, not a long press.
|
|
223
|
+
export const SIM_LONG_PRESS_DEFAULT_MS = 500
|
|
224
|
+
export const SIM_LONG_PRESS_MAX_MS = 5_000
|
|
225
|
+
|
|
219
226
|
export const SEMANTIC_READY_CONTENT_NODE_FLOOR = 100
|
|
220
227
|
export const SEMANTIC_READY_NODE_STABLE_MS = 750
|
|
221
228
|
export const SEMANTIC_READY_POLL_MS = 150
|
package/src/cloud-contract.ts
CHANGED
|
@@ -1,8 +1,37 @@
|
|
|
1
|
+
import type { BridgeCommandType } from './bridge-contract'
|
|
2
|
+
|
|
1
3
|
export const RNX_CLOUD_MAX_ARTIFACT_BYTES = 20 * 1024 * 1024
|
|
2
4
|
export const RNX_CLOUD_DESIGN_PARALLEL_JOB_LIMIT = 16
|
|
3
5
|
export const RNX_CLOUD_DESIGN_MAX_STORAGE_ENTRIES = 256
|
|
4
6
|
export const RNX_CLOUD_DESIGN_MAX_STORAGE_BYTES = 1024 * 1024
|
|
5
7
|
|
|
8
|
+
export const RNX_CLOUD_COMMAND_TYPES = Object.freeze([
|
|
9
|
+
'state',
|
|
10
|
+
'query',
|
|
11
|
+
'resolve',
|
|
12
|
+
'perform',
|
|
13
|
+
'waitFor',
|
|
14
|
+
'reset',
|
|
15
|
+
'screenshot',
|
|
16
|
+
'capture',
|
|
17
|
+
'captureRegions',
|
|
18
|
+
'settle',
|
|
19
|
+
'setAppearance',
|
|
20
|
+
'openUrl',
|
|
21
|
+
'tree',
|
|
22
|
+
'tap',
|
|
23
|
+
'longPress',
|
|
24
|
+
'memory',
|
|
25
|
+
]) satisfies readonly BridgeCommandType[]
|
|
26
|
+
|
|
27
|
+
const RNX_CLOUD_COMMAND_TYPE_SET = new Set<string>(RNX_CLOUD_COMMAND_TYPES)
|
|
28
|
+
|
|
29
|
+
export function isRnxCloudCommandType(
|
|
30
|
+
value: string,
|
|
31
|
+
): value is (typeof RNX_CLOUD_COMMAND_TYPES)[number] {
|
|
32
|
+
return RNX_CLOUD_COMMAND_TYPE_SET.has(value)
|
|
33
|
+
}
|
|
34
|
+
|
|
6
35
|
export interface RnxCloudClaim {
|
|
7
36
|
id: string
|
|
8
37
|
expiresAt: number
|
|
@@ -49,3 +49,203 @@ export function readRNXMetroModuleIdentity(
|
|
|
49
49
|
}
|
|
50
50
|
return { source: bundleText.slice(0, footerStart), modulePaths }
|
|
51
51
|
}
|
|
52
|
+
|
|
53
|
+
// the exact layout Metro's baseJSBundle emits and the rnx runtime contract
|
|
54
|
+
// depends on: a prelude, then one `__d(` or `__r(` statement per line, each
|
|
55
|
+
// running until the next statement starts its own line. one reader owns this
|
|
56
|
+
// recognition so the loader's identity validation, the source-map attribution
|
|
57
|
+
// pass, and the cloud artifact producer cannot drift apart.
|
|
58
|
+
export interface RNXMetroDefine {
|
|
59
|
+
kind: 'define'
|
|
60
|
+
moduleId: number
|
|
61
|
+
start: number
|
|
62
|
+
end: number
|
|
63
|
+
lineIndex: number
|
|
64
|
+
endLineIndex: number
|
|
65
|
+
/** the factory expression, between `__d(` and the module ID. */
|
|
66
|
+
factory: { start: number; end: number }
|
|
67
|
+
/** the dependency array, brackets included. */
|
|
68
|
+
dependencies: { start: number; end: number }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface RNXMetroRun {
|
|
72
|
+
kind: 'run'
|
|
73
|
+
start: number
|
|
74
|
+
end: number
|
|
75
|
+
lineIndex: number
|
|
76
|
+
endLineIndex: number
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export type RNXMetroStatement = RNXMetroDefine | RNXMetroRun
|
|
80
|
+
|
|
81
|
+
export interface RNXMetroBundleLayout {
|
|
82
|
+
/** everything before the first `__d(`/`__r(` line: prelude vars and polyfills. */
|
|
83
|
+
prelude: string
|
|
84
|
+
statements: RNXMetroStatement[]
|
|
85
|
+
/** `__d(` statements whose module ID could not be read. */
|
|
86
|
+
moduleIdFailures: number
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface DefineTail {
|
|
90
|
+
moduleId: number
|
|
91
|
+
factoryEnd: number
|
|
92
|
+
dependenciesStart: number
|
|
93
|
+
dependenciesEnd: number
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// each `__d(...)` ends with `),<id>,[dep,dep,...]);?`. the dependency array can
|
|
97
|
+
// be hundreds of entries long, so walk back from `end` instead of searching a
|
|
98
|
+
// fixed tail window: skip trailing `;` and whitespace, consume the closing `)`,
|
|
99
|
+
// balance `[...]` to find its opening bracket, then read the integer before it.
|
|
100
|
+
function readDefineTail(source: string, start: number, end: number): DefineTail | null {
|
|
101
|
+
let cursor = end
|
|
102
|
+
while (
|
|
103
|
+
cursor > start &&
|
|
104
|
+
(source[cursor] === ' ' ||
|
|
105
|
+
source[cursor] === '\n' ||
|
|
106
|
+
source[cursor] === '\t' ||
|
|
107
|
+
source[cursor] === ';')
|
|
108
|
+
) {
|
|
109
|
+
cursor--
|
|
110
|
+
}
|
|
111
|
+
if (source[cursor] !== ')') return null
|
|
112
|
+
cursor--
|
|
113
|
+
while (cursor > start && /\s/.test(source[cursor])) cursor--
|
|
114
|
+
if (source[cursor] !== ']') return null
|
|
115
|
+
const dependenciesEnd = cursor + 1
|
|
116
|
+
let depth = 1
|
|
117
|
+
cursor--
|
|
118
|
+
while (cursor > start && depth > 0) {
|
|
119
|
+
const character = source[cursor]
|
|
120
|
+
if (character === ']') depth++
|
|
121
|
+
else if (character === '[') depth--
|
|
122
|
+
if (depth === 0) break
|
|
123
|
+
cursor--
|
|
124
|
+
}
|
|
125
|
+
if (depth !== 0) return null
|
|
126
|
+
const dependenciesStart = cursor
|
|
127
|
+
let idEnd = cursor - 1
|
|
128
|
+
while (idEnd > start && /\s/.test(source[idEnd])) idEnd--
|
|
129
|
+
if (source[idEnd] !== ',') return null
|
|
130
|
+
// a minifier is free to write the module ID as `1e3` or `0x3e8`, so read the
|
|
131
|
+
// whole numeric literal rather than a run of decimal digits. the factory
|
|
132
|
+
// before it always ends in `}` or `)`, which stops this walk immediately.
|
|
133
|
+
let idStart = idEnd
|
|
134
|
+
while (idStart > start && /[0-9a-fA-FxX.+\-]/.test(source[idStart - 1])) idStart--
|
|
135
|
+
const literal = source.slice(idStart, idEnd)
|
|
136
|
+
if (!/^-?(?:0[xXbBoO][0-9a-fA-F]+|\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)$/.test(literal)) {
|
|
137
|
+
return null
|
|
138
|
+
}
|
|
139
|
+
const moduleId = Number(literal)
|
|
140
|
+
if (!Number.isSafeInteger(moduleId)) return null
|
|
141
|
+
let factoryEnd = idStart - 1
|
|
142
|
+
while (factoryEnd > start && /\s/.test(source[factoryEnd])) factoryEnd--
|
|
143
|
+
if (source[factoryEnd] !== ',') return null
|
|
144
|
+
return {
|
|
145
|
+
moduleId,
|
|
146
|
+
factoryEnd,
|
|
147
|
+
dependenciesStart,
|
|
148
|
+
dependenciesEnd,
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** reads the canonical Metro production layout from footer-stripped source. */
|
|
153
|
+
export function readRNXMetroBundleLayout(source: string): RNXMetroBundleLayout {
|
|
154
|
+
const lineOffsets = [0]
|
|
155
|
+
for (let index = 0; index < source.length; index++) {
|
|
156
|
+
if (source.charCodeAt(index) === 10) lineOffsets.push(index + 1)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const boundaries: number[] = []
|
|
160
|
+
for (let lineIndex = 0; lineIndex < lineOffsets.length; lineIndex++) {
|
|
161
|
+
const start = lineOffsets[lineIndex]
|
|
162
|
+
if (source.startsWith('__d(', start) || source.startsWith('__r(', start)) {
|
|
163
|
+
boundaries.push(lineIndex)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const statements: RNXMetroStatement[] = []
|
|
168
|
+
let moduleIdFailures = 0
|
|
169
|
+
for (let index = 0; index < boundaries.length; index++) {
|
|
170
|
+
const lineIndex = boundaries[index]
|
|
171
|
+
const start = lineOffsets[lineIndex]
|
|
172
|
+
const nextLineIndex = boundaries[index + 1]
|
|
173
|
+
const endLineIndex =
|
|
174
|
+
nextLineIndex !== undefined ? nextLineIndex - 1 : lineOffsets.length - 1
|
|
175
|
+
const end = nextLineIndex !== undefined ? lineOffsets[nextLineIndex] : source.length
|
|
176
|
+
if (!source.startsWith('__d(', start)) {
|
|
177
|
+
statements.push({ kind: 'run', start, end, lineIndex, endLineIndex })
|
|
178
|
+
continue
|
|
179
|
+
}
|
|
180
|
+
const tail = readDefineTail(source, start, end - 1)
|
|
181
|
+
if (!tail) {
|
|
182
|
+
moduleIdFailures++
|
|
183
|
+
continue
|
|
184
|
+
}
|
|
185
|
+
statements.push({
|
|
186
|
+
kind: 'define',
|
|
187
|
+
moduleId: tail.moduleId,
|
|
188
|
+
start,
|
|
189
|
+
end,
|
|
190
|
+
lineIndex,
|
|
191
|
+
endLineIndex,
|
|
192
|
+
factory: { start: start + '__d('.length, end: tail.factoryEnd },
|
|
193
|
+
dependencies: { start: tail.dependenciesStart, end: tail.dependenciesEnd },
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
prelude: source.slice(
|
|
199
|
+
0,
|
|
200
|
+
boundaries.length > 0 ? lineOffsets[boundaries[0]] : source.length,
|
|
201
|
+
),
|
|
202
|
+
statements,
|
|
203
|
+
moduleIdFailures,
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* validates the exact identity contract before a loader installs a Metro
|
|
209
|
+
* factory trap or a producer ships an artifact: every define carries a unique,
|
|
210
|
+
* readable module ID, and the identity map names those IDs and no others.
|
|
211
|
+
* serializers that emit another layout need an ingestion adapter that
|
|
212
|
+
* normalizes them to this format.
|
|
213
|
+
*/
|
|
214
|
+
export function validateRNXMetroModulePaths(
|
|
215
|
+
source: string,
|
|
216
|
+
modulePaths: RNXMetroModulePathMap,
|
|
217
|
+
): void {
|
|
218
|
+
const layout = readRNXMetroBundleLayout(source)
|
|
219
|
+
if (layout.moduleIdFailures > 0) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Metro module identity could not read ${layout.moduleIdFailures} module IDs`,
|
|
222
|
+
)
|
|
223
|
+
}
|
|
224
|
+
const definedIds = new Set<number>()
|
|
225
|
+
for (const statement of layout.statements) {
|
|
226
|
+
if (statement.kind !== 'define') continue
|
|
227
|
+
if (definedIds.has(statement.moduleId)) {
|
|
228
|
+
throw new Error(`Metro module identity has duplicate module ${statement.moduleId}`)
|
|
229
|
+
}
|
|
230
|
+
definedIds.add(statement.moduleId)
|
|
231
|
+
if (!Object.prototype.hasOwnProperty.call(modulePaths, String(statement.moduleId))) {
|
|
232
|
+
throw new Error(`Metro module identity is missing ${statement.moduleId}`)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (definedIds.size === 0) {
|
|
236
|
+
throw new Error('Metro module identity found no canonical JavaScript modules')
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for (const [key, modulePath] of Object.entries(modulePaths)) {
|
|
240
|
+
const moduleId = Number(key)
|
|
241
|
+
if (!Number.isSafeInteger(moduleId) || String(moduleId) !== key) {
|
|
242
|
+
throw new Error(`Metro module identity has invalid module ID ${key}`)
|
|
243
|
+
}
|
|
244
|
+
if (typeof modulePath !== 'string' || modulePath.length === 0) {
|
|
245
|
+
throw new Error(`Metro module identity has no path for ${key}`)
|
|
246
|
+
}
|
|
247
|
+
if (!definedIds.has(moduleId)) {
|
|
248
|
+
throw new Error(`Metro module identity has no module ${key}`)
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|