rnxsim 0.1.393 → 0.1.395
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 +12 -5
- 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 +25 -55
- 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 -8
- package/dist-lib/metro-production-bundle.mjs +129 -6
- package/dist-lib/metro.cjs +3 -55
- 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-plugin.ts +17 -98
- package/src/metro-production-bundle.ts +204 -3
- package/dist-lib/metro-host-modules/react-dom.js +0 -6
- package/dist-lib/metro-host-modules/react-native-web.js +0 -6
- package/dist-lib/metro-host-modules/react-native.js +0 -6
- package/dist-lib/metro-host-modules/react.js +0 -6
- package/dist-lib/metro-host-modules/react__jsx-dev-runtime.js +0 -6
- package/dist-lib/metro-host-modules/react__jsx-runtime.js +0 -6
- package/src/metro-host-modules/react-dom.js +0 -6
- package/src/metro-host-modules/react-native-web.js +0 -6
- package/src/metro-host-modules/react-native.js +0 -6
- package/src/metro-host-modules/react.js +0 -6
- package/src/metro-host-modules/react__jsx-dev-runtime.js +0 -6
- package/src/metro-host-modules/react__jsx-runtime.js +0 -6
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export const RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION = 'rnxsim'
|
|
2
1
|
const RNXSIM_METRO_MODULE_PATHS_PREFIX = 'globalThis.__sootsimModulePaths='
|
|
3
2
|
|
|
4
3
|
export type RNXMetroModulePathMap = Record<string, string>
|
|
@@ -7,14 +6,16 @@ export interface RNXMetroModuleIdentity {
|
|
|
7
6
|
modulePaths: RNXMetroModulePathMap
|
|
8
7
|
}
|
|
9
8
|
|
|
9
|
+
// the production bundle rnx loads is the app's ORDINARY production bundle.
|
|
10
|
+
// nothing about resolution changes, so these are plain Metro production
|
|
11
|
+
// options and the artifact is byte-identical to what the app ships apart
|
|
12
|
+
// from the identity footer the plugin's serializer appends.
|
|
10
13
|
export function toRNXProductionBundleUrl(bundleUrl: string): string {
|
|
11
14
|
const relative = bundleUrl.startsWith('/')
|
|
12
15
|
const url = new URL(bundleUrl, 'http://rnxsim.local')
|
|
13
16
|
url.searchParams.set('dev', 'false')
|
|
14
17
|
url.searchParams.set('hot', 'false')
|
|
15
18
|
url.searchParams.set('minify', 'true')
|
|
16
|
-
url.searchParams.set(`resolver.${RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION}`, 'true')
|
|
17
|
-
url.searchParams.set(`transform.${RNXSIM_METRO_PRODUCTION_BUNDLE_OPTION}`, 'true')
|
|
18
19
|
return relative ? `${url.pathname}${url.search}${url.hash}` : url.toString()
|
|
19
20
|
}
|
|
20
21
|
|
|
@@ -49,3 +50,203 @@ export function readRNXMetroModuleIdentity(
|
|
|
49
50
|
}
|
|
50
51
|
return { source: bundleText.slice(0, footerStart), modulePaths }
|
|
51
52
|
}
|
|
53
|
+
|
|
54
|
+
// the exact layout Metro's baseJSBundle emits and the rnx runtime contract
|
|
55
|
+
// depends on: a prelude, then one `__d(` or `__r(` statement per line, each
|
|
56
|
+
// running until the next statement starts its own line. one reader owns this
|
|
57
|
+
// recognition so the loader's identity validation, the source-map attribution
|
|
58
|
+
// pass, and the cloud artifact producer cannot drift apart.
|
|
59
|
+
export interface RNXMetroDefine {
|
|
60
|
+
kind: 'define'
|
|
61
|
+
moduleId: number
|
|
62
|
+
start: number
|
|
63
|
+
end: number
|
|
64
|
+
lineIndex: number
|
|
65
|
+
endLineIndex: number
|
|
66
|
+
/** the factory expression, between `__d(` and the module ID. */
|
|
67
|
+
factory: { start: number; end: number }
|
|
68
|
+
/** the dependency array, brackets included. */
|
|
69
|
+
dependencies: { start: number; end: number }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface RNXMetroRun {
|
|
73
|
+
kind: 'run'
|
|
74
|
+
start: number
|
|
75
|
+
end: number
|
|
76
|
+
lineIndex: number
|
|
77
|
+
endLineIndex: number
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type RNXMetroStatement = RNXMetroDefine | RNXMetroRun
|
|
81
|
+
|
|
82
|
+
export interface RNXMetroBundleLayout {
|
|
83
|
+
/** everything before the first `__d(`/`__r(` line: prelude vars and polyfills. */
|
|
84
|
+
prelude: string
|
|
85
|
+
statements: RNXMetroStatement[]
|
|
86
|
+
/** `__d(` statements whose module ID could not be read. */
|
|
87
|
+
moduleIdFailures: number
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
interface DefineTail {
|
|
91
|
+
moduleId: number
|
|
92
|
+
factoryEnd: number
|
|
93
|
+
dependenciesStart: number
|
|
94
|
+
dependenciesEnd: number
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// each `__d(...)` ends with `),<id>,[dep,dep,...]);?`. the dependency array can
|
|
98
|
+
// be hundreds of entries long, so walk back from `end` instead of searching a
|
|
99
|
+
// fixed tail window: skip trailing `;` and whitespace, consume the closing `)`,
|
|
100
|
+
// balance `[...]` to find its opening bracket, then read the integer before it.
|
|
101
|
+
function readDefineTail(source: string, start: number, end: number): DefineTail | null {
|
|
102
|
+
let cursor = end
|
|
103
|
+
while (
|
|
104
|
+
cursor > start &&
|
|
105
|
+
(source[cursor] === ' ' ||
|
|
106
|
+
source[cursor] === '\n' ||
|
|
107
|
+
source[cursor] === '\t' ||
|
|
108
|
+
source[cursor] === ';')
|
|
109
|
+
) {
|
|
110
|
+
cursor--
|
|
111
|
+
}
|
|
112
|
+
if (source[cursor] !== ')') return null
|
|
113
|
+
cursor--
|
|
114
|
+
while (cursor > start && /\s/.test(source[cursor])) cursor--
|
|
115
|
+
if (source[cursor] !== ']') return null
|
|
116
|
+
const dependenciesEnd = cursor + 1
|
|
117
|
+
let depth = 1
|
|
118
|
+
cursor--
|
|
119
|
+
while (cursor > start && depth > 0) {
|
|
120
|
+
const character = source[cursor]
|
|
121
|
+
if (character === ']') depth++
|
|
122
|
+
else if (character === '[') depth--
|
|
123
|
+
if (depth === 0) break
|
|
124
|
+
cursor--
|
|
125
|
+
}
|
|
126
|
+
if (depth !== 0) return null
|
|
127
|
+
const dependenciesStart = cursor
|
|
128
|
+
let idEnd = cursor - 1
|
|
129
|
+
while (idEnd > start && /\s/.test(source[idEnd])) idEnd--
|
|
130
|
+
if (source[idEnd] !== ',') return null
|
|
131
|
+
// a minifier is free to write the module ID as `1e3` or `0x3e8`, so read the
|
|
132
|
+
// whole numeric literal rather than a run of decimal digits. the factory
|
|
133
|
+
// before it always ends in `}` or `)`, which stops this walk immediately.
|
|
134
|
+
let idStart = idEnd
|
|
135
|
+
while (idStart > start && /[0-9a-fA-FxX.+\-]/.test(source[idStart - 1])) idStart--
|
|
136
|
+
const literal = source.slice(idStart, idEnd)
|
|
137
|
+
if (!/^-?(?:0[xXbBoO][0-9a-fA-F]+|\d+(?:\.\d*)?(?:[eE][+-]?\d+)?)$/.test(literal)) {
|
|
138
|
+
return null
|
|
139
|
+
}
|
|
140
|
+
const moduleId = Number(literal)
|
|
141
|
+
if (!Number.isSafeInteger(moduleId)) return null
|
|
142
|
+
let factoryEnd = idStart - 1
|
|
143
|
+
while (factoryEnd > start && /\s/.test(source[factoryEnd])) factoryEnd--
|
|
144
|
+
if (source[factoryEnd] !== ',') return null
|
|
145
|
+
return {
|
|
146
|
+
moduleId,
|
|
147
|
+
factoryEnd,
|
|
148
|
+
dependenciesStart,
|
|
149
|
+
dependenciesEnd,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** reads the canonical Metro production layout from footer-stripped source. */
|
|
154
|
+
export function readRNXMetroBundleLayout(source: string): RNXMetroBundleLayout {
|
|
155
|
+
const lineOffsets = [0]
|
|
156
|
+
for (let index = 0; index < source.length; index++) {
|
|
157
|
+
if (source.charCodeAt(index) === 10) lineOffsets.push(index + 1)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const boundaries: number[] = []
|
|
161
|
+
for (let lineIndex = 0; lineIndex < lineOffsets.length; lineIndex++) {
|
|
162
|
+
const start = lineOffsets[lineIndex]
|
|
163
|
+
if (source.startsWith('__d(', start) || source.startsWith('__r(', start)) {
|
|
164
|
+
boundaries.push(lineIndex)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const statements: RNXMetroStatement[] = []
|
|
169
|
+
let moduleIdFailures = 0
|
|
170
|
+
for (let index = 0; index < boundaries.length; index++) {
|
|
171
|
+
const lineIndex = boundaries[index]
|
|
172
|
+
const start = lineOffsets[lineIndex]
|
|
173
|
+
const nextLineIndex = boundaries[index + 1]
|
|
174
|
+
const endLineIndex =
|
|
175
|
+
nextLineIndex !== undefined ? nextLineIndex - 1 : lineOffsets.length - 1
|
|
176
|
+
const end = nextLineIndex !== undefined ? lineOffsets[nextLineIndex] : source.length
|
|
177
|
+
if (!source.startsWith('__d(', start)) {
|
|
178
|
+
statements.push({ kind: 'run', start, end, lineIndex, endLineIndex })
|
|
179
|
+
continue
|
|
180
|
+
}
|
|
181
|
+
const tail = readDefineTail(source, start, end - 1)
|
|
182
|
+
if (!tail) {
|
|
183
|
+
moduleIdFailures++
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
statements.push({
|
|
187
|
+
kind: 'define',
|
|
188
|
+
moduleId: tail.moduleId,
|
|
189
|
+
start,
|
|
190
|
+
end,
|
|
191
|
+
lineIndex,
|
|
192
|
+
endLineIndex,
|
|
193
|
+
factory: { start: start + '__d('.length, end: tail.factoryEnd },
|
|
194
|
+
dependencies: { start: tail.dependenciesStart, end: tail.dependenciesEnd },
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
prelude: source.slice(
|
|
200
|
+
0,
|
|
201
|
+
boundaries.length > 0 ? lineOffsets[boundaries[0]] : source.length,
|
|
202
|
+
),
|
|
203
|
+
statements,
|
|
204
|
+
moduleIdFailures,
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* validates the exact identity contract before a loader installs a Metro
|
|
210
|
+
* factory trap or a producer ships an artifact: every define carries a unique,
|
|
211
|
+
* readable module ID, and the identity map names those IDs and no others.
|
|
212
|
+
* serializers that emit another layout need an ingestion adapter that
|
|
213
|
+
* normalizes them to this format.
|
|
214
|
+
*/
|
|
215
|
+
export function validateRNXMetroModulePaths(
|
|
216
|
+
source: string,
|
|
217
|
+
modulePaths: RNXMetroModulePathMap,
|
|
218
|
+
): void {
|
|
219
|
+
const layout = readRNXMetroBundleLayout(source)
|
|
220
|
+
if (layout.moduleIdFailures > 0) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Metro module identity could not read ${layout.moduleIdFailures} module IDs`,
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
const definedIds = new Set<number>()
|
|
226
|
+
for (const statement of layout.statements) {
|
|
227
|
+
if (statement.kind !== 'define') continue
|
|
228
|
+
if (definedIds.has(statement.moduleId)) {
|
|
229
|
+
throw new Error(`Metro module identity has duplicate module ${statement.moduleId}`)
|
|
230
|
+
}
|
|
231
|
+
definedIds.add(statement.moduleId)
|
|
232
|
+
if (!Object.prototype.hasOwnProperty.call(modulePaths, String(statement.moduleId))) {
|
|
233
|
+
throw new Error(`Metro module identity is missing ${statement.moduleId}`)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (definedIds.size === 0) {
|
|
237
|
+
throw new Error('Metro module identity found no canonical JavaScript modules')
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
for (const [key, modulePath] of Object.entries(modulePaths)) {
|
|
241
|
+
const moduleId = Number(key)
|
|
242
|
+
if (!Number.isSafeInteger(moduleId) || String(moduleId) !== key) {
|
|
243
|
+
throw new Error(`Metro module identity has invalid module ID ${key}`)
|
|
244
|
+
}
|
|
245
|
+
if (typeof modulePath !== 'string' || modulePath.length === 0) {
|
|
246
|
+
throw new Error(`Metro module identity has no path for ${key}`)
|
|
247
|
+
}
|
|
248
|
+
if (!definedIds.has(moduleId)) {
|
|
249
|
+
throw new Error(`Metro module identity has no module ${key}`)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|