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.
Files changed (69) hide show
  1. package/README.md +12 -5
  2. package/cli/cloud-client.ts +74 -52
  3. package/cli/cloud-dispatch.ts +2 -1
  4. package/cli/commands/inspect/core.ts +33 -2
  5. package/cli/commands/platform.ts +34 -9
  6. package/dist-lib/agent-daemon-client.cjs +1 -1
  7. package/dist-lib/agent-events.cjs +1 -1
  8. package/dist-lib/agent-identity.cjs +1 -1
  9. package/dist-lib/agent-sessions.cjs +1 -1
  10. package/dist-lib/attached-projects.cjs +1 -1
  11. package/dist-lib/auth/shared-session.cjs +1 -1
  12. package/dist-lib/backend-origin.cjs +1 -1
  13. package/dist-lib/beta.cjs +1 -1
  14. package/dist-lib/beta.mjs +1 -1
  15. package/dist-lib/bridge-constants.cjs +1 -1
  16. package/dist-lib/bridge-contract-input.cjs +12 -1
  17. package/dist-lib/bridge-contract-input.mjs +10 -1
  18. package/dist-lib/bridge-contract.cjs +7 -1
  19. package/dist-lib/bridge-contract.mjs +5 -1
  20. package/dist-lib/capture-contract.cjs +1 -1
  21. package/dist-lib/capture-contract.mjs +1 -1
  22. package/dist-lib/cli-constants.cjs +1 -1
  23. package/dist-lib/cloud-contract.cjs +29 -3
  24. package/dist-lib/cloud-contract.mjs +26 -2
  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 +1 -1
  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 +25 -55
  36. package/dist-lib/jump-to-source-babel.cjs +1 -1
  37. package/dist-lib/menu.cjs +1 -1
  38. package/dist-lib/menu.mjs +1 -1
  39. package/dist-lib/metro-production-bundle.cjs +132 -8
  40. package/dist-lib/metro-production-bundle.mjs +129 -6
  41. package/dist-lib/metro.cjs +3 -55
  42. package/dist-lib/profiles.cjs +1 -1
  43. package/dist-lib/public-brand.cjs +1 -1
  44. package/dist-lib/react-native-host-modules.cjs +1 -1
  45. package/dist-lib/react-native-host-modules.mjs +1 -1
  46. package/dist-lib/render-mode.cjs +1 -1
  47. package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
  48. package/dist-lib/sdk.cjs +24 -3
  49. package/dist-lib/sdk.mjs +24 -3
  50. package/dist-lib/skills.cjs +28 -25
  51. package/dist-lib/vite.cjs +1 -1
  52. package/package.json +1 -1
  53. package/src/bridge-contract-input.ts +12 -0
  54. package/src/bridge-contract.ts +7 -0
  55. package/src/cloud-contract.ts +29 -0
  56. package/src/metro-plugin.ts +17 -98
  57. package/src/metro-production-bundle.ts +204 -3
  58. package/dist-lib/metro-host-modules/react-dom.js +0 -6
  59. package/dist-lib/metro-host-modules/react-native-web.js +0 -6
  60. package/dist-lib/metro-host-modules/react-native.js +0 -6
  61. package/dist-lib/metro-host-modules/react.js +0 -6
  62. package/dist-lib/metro-host-modules/react__jsx-dev-runtime.js +0 -6
  63. package/dist-lib/metro-host-modules/react__jsx-runtime.js +0 -6
  64. package/src/metro-host-modules/react-dom.js +0 -6
  65. package/src/metro-host-modules/react-native-web.js +0 -6
  66. package/src/metro-host-modules/react-native.js +0 -6
  67. package/src/metro-host-modules/react.js +0 -6
  68. package/src/metro-host-modules/react__jsx-dev-runtime.js +0 -6
  69. 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
+ }
@@ -1,6 +0,0 @@
1
- const name = 'react-dom'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react-native-web'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react-native'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react/jsx-dev-runtime'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react/jsx-runtime'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react-dom'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react-native-web'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react-native'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react/jsx-dev-runtime'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]
@@ -1,6 +0,0 @@
1
- const name = 'react/jsx-runtime'
2
- const host = globalThis.__contrastHostModules
3
- if (!host || !(name in host)) {
4
- throw new Error(`rnxsim: runtime did not publish __contrastHostModules[${name}]`)
5
- }
6
- module.exports = host[name]