peachsim 0.1.527
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/LICENSE +22 -0
- package/README.md +640 -0
- package/cli/app-config.ts +82 -0
- package/cli/app-fonts.ts +388 -0
- package/cli/app-project.ts +59 -0
- package/cli/app-splash.ts +171 -0
- package/cli/app-state-reset.ts +26 -0
- package/cli/auth.ts +184 -0
- package/cli/bin.ts +59 -0
- package/cli/bridge-diagnostics.ts +226 -0
- package/cli/bridge-flow-runner.ts +2806 -0
- package/cli/browser-backend.ts +44 -0
- package/cli/browser-evals.ts +96 -0
- package/cli/cli-update.ts +261 -0
- package/cli/cloud-client.ts +1530 -0
- package/cli/cloud-dispatch.ts +206 -0
- package/cli/cloud-session.ts +227 -0
- package/cli/command-contract.ts +221 -0
- package/cli/command-registry.ts +304 -0
- package/cli/commands/agent-wrapper.ts +1048 -0
- package/cli/commands/agent.ts +439 -0
- package/cli/commands/app-fonts.ts +99 -0
- package/cli/commands/assert.ts +542 -0
- package/cli/commands/auth.ts +71 -0
- package/cli/commands/box/bundle-plane.ts +71 -0
- package/cli/commands/box/checkout-plane.ts +256 -0
- package/cli/commands/box/plane-share.ts +208 -0
- package/cli/commands/box/rnx-command.ts +60 -0
- package/cli/commands/box/search-command.ts +157 -0
- package/cli/commands/box.ts +925 -0
- package/cli/commands/camera.ts +266 -0
- package/cli/commands/cleanup.ts +169 -0
- package/cli/commands/compat.ts +176 -0
- package/cli/commands/config.ts +34 -0
- package/cli/commands/control.ts +2154 -0
- package/cli/commands/cpu-profile.ts +941 -0
- package/cli/commands/daemon-mac-app.ts +165 -0
- package/cli/commands/daemon.ts +800 -0
- package/cli/commands/debug.ts +751 -0
- package/cli/commands/desktop.ts +39 -0
- package/cli/commands/detect.ts +201 -0
- package/cli/commands/detox.ts +481 -0
- package/cli/commands/device.ts +134 -0
- package/cli/commands/diagnose.ts +589 -0
- package/cli/commands/do-chain.ts +408 -0
- package/cli/commands/electron.ts +94 -0
- package/cli/commands/film.ts +379 -0
- package/cli/commands/flow.ts +1240 -0
- package/cli/commands/inspect/actions.ts +516 -0
- package/cli/commands/inspect/core.ts +2086 -0
- package/cli/commands/inspect/count.ts +17 -0
- package/cli/commands/inspect/describe.ts +119 -0
- package/cli/commands/inspect/env.ts +25 -0
- package/cli/commands/inspect/find.ts +169 -0
- package/cli/commands/inspect/get-layout.ts +53 -0
- package/cli/commands/inspect/keyboard.ts +54 -0
- package/cli/commands/inspect/list.ts +175 -0
- package/cli/commands/inspect/memory.ts +215 -0
- package/cli/commands/inspect/redaction.ts +39 -0
- package/cli/commands/inspect/resolve-target.ts +77 -0
- package/cli/commands/inspect/screens.ts +78 -0
- package/cli/commands/inspect/settle.ts +32 -0
- package/cli/commands/inspect/settling.ts +197 -0
- package/cli/commands/inspect/shared.ts +259 -0
- package/cli/commands/inspect/sleep.ts +15 -0
- package/cli/commands/inspect/tree.ts +23 -0
- package/cli/commands/inspect/url.ts +17 -0
- package/cli/commands/inspect/wait-event.ts +211 -0
- package/cli/commands/inspect/wait-idle.ts +25 -0
- package/cli/commands/inspect/wait-ready.ts +87 -0
- package/cli/commands/inspect/wait-selector.ts +59 -0
- package/cli/commands/inspect/wait.ts +32 -0
- package/cli/commands/inspect.ts +4738 -0
- package/cli/commands/install-cli.ts +275 -0
- package/cli/commands/install-desktop.ts +385 -0
- package/cli/commands/login.ts +357 -0
- package/cli/commands/logout.ts +24 -0
- package/cli/commands/maestro-generate.ts +375 -0
- package/cli/commands/maestro.ts +536 -0
- package/cli/commands/mode.ts +58 -0
- package/cli/commands/no-bridge-hint.ts +80 -0
- package/cli/commands/perf.ts +67 -0
- package/cli/commands/permissions.ts +203 -0
- package/cli/commands/platform.ts +410 -0
- package/cli/commands/preview.ts +103 -0
- package/cli/commands/profile.ts +106 -0
- package/cli/commands/react.ts +353 -0
- package/cli/commands/record.ts +1435 -0
- package/cli/commands/report-issue.ts +305 -0
- package/cli/commands/reset.ts +89 -0
- package/cli/commands/runtime.ts +348 -0
- package/cli/commands/screenshot-command.ts +106 -0
- package/cli/commands/screenshot-layers.ts +144 -0
- package/cli/commands/screenshot-mode.ts +38 -0
- package/cli/commands/screenshot.ts +492 -0
- package/cli/commands/screenshots-capture.ts +608 -0
- package/cli/commands/screenshots.ts +127 -0
- package/cli/commands/serve.ts +139 -0
- package/cli/commands/setup.ts +416 -0
- package/cli/commands/shell-boolean-mode.ts +82 -0
- package/cli/commands/skills.ts +468 -0
- package/cli/commands/slides.ts +357 -0
- package/cli/commands/state.ts +94 -0
- package/cli/commands/storage.ts +58 -0
- package/cli/commands/telemetry.ts +55 -0
- package/cli/commands/test.ts +535 -0
- package/cli/commands/three-mode.ts +771 -0
- package/cli/commands/timeline.ts +121 -0
- package/cli/commands/upgrade.ts +167 -0
- package/cli/commands/upload.ts +1203 -0
- package/cli/commands/version.ts +63 -0
- package/cli/commands/welcome.ts +17 -0
- package/cli/commands/what-happened.ts +328 -0
- package/cli/current-sim.ts +217 -0
- package/cli/desktop-companion.ts +315 -0
- package/cli/drivers/electron.ts +70 -0
- package/cli/drivers/index.ts +20 -0
- package/cli/drivers/playwright-provisioning.ts +184 -0
- package/cli/drivers/playwright-sim-host.ts +537 -0
- package/cli/drivers/playwright.ts +209 -0
- package/cli/drivers/registry.ts +65 -0
- package/cli/drivers/types.ts +102 -0
- package/cli/flow-export.ts +55 -0
- package/cli/flow-file.ts +142 -0
- package/cli/flow-live-status.ts +126 -0
- package/cli/flow-session.ts +140 -0
- package/cli/help-core.ts +143 -0
- package/cli/help.ts +44 -0
- package/cli/hidden-runtime-alias.ts +19 -0
- package/cli/hints.ts +214 -0
- package/cli/inspect-notice-state.ts +114 -0
- package/cli/internal-child.ts +176 -0
- package/cli/maestro-js.ts +323 -0
- package/cli/main.ts +704 -0
- package/cli/open-url.ts +8 -0
- package/cli/outbound-endpoints.ts +830 -0
- package/cli/parent-pid.ts +204 -0
- package/cli/parse-args.ts +189 -0
- package/cli/privacy.ts +53 -0
- package/cli/prompt.ts +56 -0
- package/cli/recording-access.ts +108 -0
- package/cli/registry.ts +2 -0
- package/cli/resolve-assets.ts +63 -0
- package/cli/rnx-in-process.ts +98 -0
- package/cli/run-registry.ts +226 -0
- package/cli/run-rnx-core.ts +148 -0
- package/cli/run-rnx.ts +137 -0
- package/cli/runtime-notes.ts +66 -0
- package/cli/runtime-summary.ts +25 -0
- package/cli/secure-file.ts +31 -0
- package/cli/self-invocation.ts +58 -0
- package/cli/send-to-box.ts +109 -0
- package/cli/setup-repository.ts +191 -0
- package/cli/shell-init.ts +98 -0
- package/cli/standalone.ts +4 -0
- package/cli/telemetry.ts +84 -0
- package/cli/ui.ts +68 -0
- package/cli/ws-bridge.ts +1020 -0
- package/detox/attempt-events.cjs +23 -0
- package/detox/colors.ts +54 -0
- package/detox/config-loader.ts +135 -0
- package/detox/element-types.ts +48 -0
- package/detox/expectations.ts +534 -0
- package/detox/gestures.ts +501 -0
- package/detox/index.ts +2694 -0
- package/detox/jest-preset.cjs +29 -0
- package/detox/jest-setup-after-env.cjs +22 -0
- package/detox/matchers.ts +30 -0
- package/detox/motion-change.cjs +109 -0
- package/detox/motion-change.d.cts +35 -0
- package/detox/navigation-lifecycle.ts +290 -0
- package/detox/navigation.ts +43 -0
- package/detox/proof-frame.cjs +114 -0
- package/detox/record-block-execution.cjs +78 -0
- package/detox/screenshots/animated-color-test-rest-norngh.png +0 -0
- package/detox/screenshots/color-test-after-drag-norngh.png +0 -0
- package/detox/screenshots/color-test-rest-norngh.png +0 -0
- package/detox/screenshots/theme-blue-toggle.png +0 -0
- package/detox/screenshots/theme-blue.png +0 -0
- package/detox/screenshots/theme-red-toggle.png +0 -0
- package/detox/screenshots/theme-red.png +0 -0
- package/dist-lib/agent-daemon-client.cjs +502 -0
- package/dist-lib/agent-events.cjs +48 -0
- package/dist-lib/agent-identity.cjs +53 -0
- package/dist-lib/agent-sessions.cjs +759 -0
- package/dist-lib/attached-projects.cjs +526 -0
- package/dist-lib/auth/shared-session.cjs +208 -0
- package/dist-lib/backend-origin.cjs +88 -0
- package/dist-lib/beta.cjs +41 -0
- package/dist-lib/beta.mjs +13 -0
- package/dist-lib/bridge-constants.cjs +161 -0
- package/dist-lib/bridge-contract-input.cjs +451 -0
- package/dist-lib/bridge-contract-input.mjs +414 -0
- package/dist-lib/bridge-contract.cjs +130 -0
- package/dist-lib/bridge-contract.mjs +94 -0
- package/dist-lib/capture-contract.cjs +220 -0
- package/dist-lib/capture-contract.mjs +190 -0
- package/dist-lib/cli-constants.cjs +32 -0
- package/dist-lib/cloud-contract.cjs +194 -0
- package/dist-lib/cloud-contract.mjs +145 -0
- package/dist-lib/cloud.cjs +670 -0
- package/dist-lib/cloud.mjs +639 -0
- package/dist-lib/config.cjs +96 -0
- package/dist-lib/detox/index.cjs +3789 -0
- package/dist-lib/detox/jest-preset.cjs +29 -0
- package/dist-lib/dev-bundle-resolution.cjs +290 -0
- package/dist-lib/home-paths.cjs +638 -0
- package/dist-lib/host/bridge-host.cjs +10903 -0
- package/dist-lib/host/fetch-proxy-handler.cjs +421 -0
- package/dist-lib/host/fetch-proxy-overrides.cjs +63 -0
- package/dist-lib/host/fetch-proxy-overrides.mjs +37 -0
- package/dist-lib/host/replacement-module-handler.cjs +469 -0
- package/dist-lib/host/websocket-proxy.cjs +298 -0
- package/dist-lib/index.cjs +4268 -0
- package/dist-lib/jump-to-source-babel.cjs +93 -0
- package/dist-lib/jump-to-source-native.cjs +112 -0
- package/dist-lib/menu.cjs +646 -0
- package/dist-lib/menu.mjs +604 -0
- package/dist-lib/metro-fingerprint-registry.cjs +186 -0
- package/dist-lib/metro-fingerprint-registry.mjs +156 -0
- package/dist-lib/metro-production-bundle.cjs +329 -0
- package/dist-lib/metro-production-bundle.mjs +295 -0
- package/dist-lib/metro.cjs +904 -0
- package/dist-lib/profiles.cjs +246 -0
- package/dist-lib/public-brand.cjs +39 -0
- package/dist-lib/react-native-host-modules.cjs +51 -0
- package/dist-lib/react-native-host-modules.mjs +25 -0
- package/dist-lib/render-mode.cjs +54 -0
- package/dist-lib/scripts/dev-server-scanner.cjs +1232 -0
- package/dist-lib/sdk.cjs +2433 -0
- package/dist-lib/sdk.mjs +2336 -0
- package/dist-lib/skills.cjs +39132 -0
- package/dist-lib/swift-submit.cjs +92 -0
- package/dist-lib/swift-submit.mjs +65 -0
- package/dist-lib/swift.cjs +1646 -0
- package/dist-lib/vite.cjs +1591 -0
- package/package.json +290 -0
- package/scripts/dev-server-scanner.ts +1291 -0
- package/scripts/optional-demo-registry.ts +113 -0
- package/skills/contrast/SKILL.md +165 -0
- package/skills/rnx-debug/SKILL.md +433 -0
- package/skills/rnx-perf/SKILL.md +399 -0
- package/skills/rnx-setup/SKILL.md +172 -0
- package/skills/rnx-test/SKILL.md +321 -0
- package/skills/rnx-visual/SKILL.md +126 -0
- package/src/agent-daemon-client.ts +410 -0
- package/src/agent-events.ts +71 -0
- package/src/agent-identity.ts +23 -0
- package/src/agent-prompt.ts +71 -0
- package/src/agent-sessions.ts +580 -0
- package/src/app-url.ts +39 -0
- package/src/attached-projects.ts +539 -0
- package/src/auth/shared-session.ts +235 -0
- package/src/backend-origin.ts +70 -0
- package/src/beta.ts +19 -0
- package/src/bridge-constants.ts +175 -0
- package/src/bridge-contract-input.ts +502 -0
- package/src/bridge-contract.ts +558 -0
- package/src/browser-cache.ts +16 -0
- package/src/capture-contract.ts +587 -0
- package/src/cli-constants.ts +1 -0
- package/src/cli-version.ts +40 -0
- package/src/cloud-contract.ts +274 -0
- package/src/cloud.ts +762 -0
- package/src/codex-client.ts +234 -0
- package/src/config.ts +132 -0
- package/src/connect.ts +101 -0
- package/src/dev-bundle-resolution.ts +346 -0
- package/src/dev-server-open.ts +198 -0
- package/src/disk-cleanup.ts +540 -0
- package/src/flow-timeout-scale.ts +15 -0
- package/src/home-paths.ts +806 -0
- package/src/host/agent-host.ts +666 -0
- package/src/host/bridge-host.ts +2788 -0
- package/src/host/fetch-proxy-handler.ts +449 -0
- package/src/host/fetch-proxy-overrides.ts +51 -0
- package/src/host/open-url.ts +89 -0
- package/src/host/plane-host.ts +203 -0
- package/src/host/replacement-module-handler.ts +158 -0
- package/src/host/websocket-proxy.ts +315 -0
- package/src/index.ts +71 -0
- package/src/jump-to-source-babel.ts +135 -0
- package/src/jump-to-source-native.ts +102 -0
- package/src/menu.ts +777 -0
- package/src/metro-fingerprint-registry.ts +302 -0
- package/src/metro-plugin.ts +341 -0
- package/src/metro-production-bundle.ts +471 -0
- package/src/native-dev-bundle-url.ts +118 -0
- package/src/native-seam-manifest.ts +341 -0
- package/src/playwright-host-log.ts +16 -0
- package/src/plist.ts +27 -0
- package/src/poll-until.ts +123 -0
- package/src/profiles.ts +179 -0
- package/src/public-brand.ts +9 -0
- package/src/react-native-host-modules.ts +26 -0
- package/src/render-mode.ts +27 -0
- package/src/rolldown-plugin-swift.ts +240 -0
- package/src/runtime-assets.ts +233 -0
- package/src/runtime-delivery.ts +54 -0
- package/src/screenshots/compose.ts +422 -0
- package/src/screenshots/frame-compose.ts +514 -0
- package/src/screenshots/orchestrate.ts +256 -0
- package/src/screenshots/org-deck.ts +91 -0
- package/src/screenshots/registry.ts +58 -0
- package/src/screenshots/schema.ts +364 -0
- package/src/sdk.ts +15 -0
- package/src/settings-bundle.ts +418 -0
- package/src/shell-bridge-identity.ts +15 -0
- package/src/sim-client.ts +697 -0
- package/src/skills/builtin/a11y-review.ts +132 -0
- package/src/skills/builtin/compat-check.ts +71 -0
- package/src/skills/builtin/maestro-test.ts +121 -0
- package/src/skills/builtin/perf-profile.ts +90 -0
- package/src/skills/builtin/screenshot-all.ts +52 -0
- package/src/skills/builtin/visual-diff.ts +100 -0
- package/src/skills/registry.ts +107 -0
- package/src/skills/types.ts +41 -0
- package/src/swift-submit.ts +185 -0
- package/src/swift.ts +7 -0
- package/src/team-machine-prompt.ts +73 -0
- package/src/vite-plugin-one.ts +264 -0
- package/src/vite-plugin-swift.ts +620 -0
- package/src/vite-plugin.ts +1946 -0
- package/src/worklets-babel.ts +250 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* react-native specifiers whose upstream JavaScript must stay in a guest bundle.
|
|
3
|
+
*
|
|
4
|
+
* rnx owns the rest of the react-native package through its host-module registry.
|
|
5
|
+
* these modules instead run against globals installed by the simulator, which
|
|
6
|
+
* preserves upstream platform, style, event-emitter, and TurboModule behavior.
|
|
7
|
+
*/
|
|
8
|
+
export const REACT_NATIVE_PASSTHROUGH_SPECIFIERS: readonly string[] = [
|
|
9
|
+
'react-native/Libraries/vendor/emitter',
|
|
10
|
+
'react-native/Libraries/Components/View/ReactNativeStyleAttributes',
|
|
11
|
+
'react-native/Libraries/StyleSheet',
|
|
12
|
+
'react-native/Libraries/Utilities/differ',
|
|
13
|
+
'react-native/Libraries/Utilities/stringifySafe',
|
|
14
|
+
'react-native/src/private/assets/AssetRegistry',
|
|
15
|
+
'react-native/src/private/featureflags',
|
|
16
|
+
'react-native/Libraries/TurboModule/TurboModuleRegistry',
|
|
17
|
+
'react-native/Libraries/Utilities/Platform',
|
|
18
|
+
'react-native/Libraries/Utilities/NativePlatformConstants',
|
|
19
|
+
'react-native/src/private/specs_DEPRECATED/modules/NativePlatformConstants',
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
export function isReactNativePassthroughSpecifier(specifier: string): boolean {
|
|
23
|
+
return REACT_NATIVE_PASSTHROUGH_SPECIFIERS.some(
|
|
24
|
+
(passthrough) => specifier === passthrough || specifier.startsWith(`${passthrough}/`),
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type RenderMode = 'worker'
|
|
2
|
+
|
|
3
|
+
// worker rendering is now the only supported mode.
|
|
4
|
+
export function getRenderMode(_searchParams: URLSearchParams): RenderMode {
|
|
5
|
+
return 'worker'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isRenderWorkerMode(_searchParams: URLSearchParams): boolean {
|
|
9
|
+
return true
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isRenderMainMode(_searchParams: URLSearchParams): boolean {
|
|
13
|
+
return false
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function hasRenderWorkerBootstrap(
|
|
17
|
+
workerModuleBaseUrl: string | null | undefined,
|
|
18
|
+
): boolean {
|
|
19
|
+
return typeof workerModuleBaseUrl === 'string' && workerModuleBaseUrl.length > 0
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function shouldEnableRenderWorker(
|
|
23
|
+
_searchParams: URLSearchParams,
|
|
24
|
+
workerModuleBaseUrl: string | null | undefined,
|
|
25
|
+
): boolean {
|
|
26
|
+
return hasRenderWorkerBootstrap(workerModuleBaseUrl)
|
|
27
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// `.swift` handling for One's native rolldown engine: submit the owning
|
|
2
|
+
// swiftpm package to `/api/swift-compile` and answer every `.swift` import
|
|
3
|
+
// with the module that mounts the published artifact.
|
|
4
|
+
//
|
|
5
|
+
// this is the remote twin of `vite-plugin-swift.ts`. that plugin runs the
|
|
6
|
+
// local toolchain and serves the artifact from dev-server middleware; the
|
|
7
|
+
// native engine is its own rolldown instance with no middleware seam, so
|
|
8
|
+
// this plugin compiles nothing in-process and serves nothing. the artifact
|
|
9
|
+
// url the service answers is absolute (compile-cdn), so there is nothing to
|
|
10
|
+
// serve. same compiler, same store, same hash, same credential as the
|
|
11
|
+
// browser worker — one path, no second toolchain to install for `one dev`.
|
|
12
|
+
//
|
|
13
|
+
// an app registers it through One's native plugin seam, not `config.plugins`
|
|
14
|
+
// (which never reach the native engine):
|
|
15
|
+
//
|
|
16
|
+
// // vite.config.ts
|
|
17
|
+
// export default {
|
|
18
|
+
// plugins: [one({
|
|
19
|
+
// native: {
|
|
20
|
+
// bundler: 'vite',
|
|
21
|
+
// bundlerOptions: {
|
|
22
|
+
// plugins: [swiftRemotePlugin({
|
|
23
|
+
// endpoint: 'https://contrast.dev',
|
|
24
|
+
// assetReadToken: process.env.CONTRAST_CCDN_KEY,
|
|
25
|
+
// })],
|
|
26
|
+
// },
|
|
27
|
+
// },
|
|
28
|
+
// })],
|
|
29
|
+
// }
|
|
30
|
+
//
|
|
31
|
+
// no babel anywhere in this path: resolveId and load only, emitting an ESM
|
|
32
|
+
// module. offline `one dev` fails loud at the submit, the same as a browser
|
|
33
|
+
// build without a credential; the standalone `rnx` CLI keeps the local
|
|
34
|
+
// toolchain plugin for that case.
|
|
35
|
+
|
|
36
|
+
import fs from 'node:fs'
|
|
37
|
+
import path from 'node:path'
|
|
38
|
+
import {
|
|
39
|
+
enumerateBundleImages,
|
|
40
|
+
enumerateBundleJson,
|
|
41
|
+
} from '../../sootsim-swift/src/bundleAssets'
|
|
42
|
+
import { compileSwiftProject, httpSwiftCompileSubmit } from './swift-submit'
|
|
43
|
+
import { entryRootId } from './vite-plugin-swift'
|
|
44
|
+
import type { SwiftModuleAddress } from './swift-submit'
|
|
45
|
+
import type { Plugin } from 'vite'
|
|
46
|
+
|
|
47
|
+
const VIRTUAL_PREFIX = '\0rnx-swift-remote:'
|
|
48
|
+
|
|
49
|
+
export interface SwiftRemotePluginOptions {
|
|
50
|
+
/** origin serving `/api/swift-compile` (the Contrast app). */
|
|
51
|
+
endpoint: string
|
|
52
|
+
/** a `ccdn_` API key or dev session token the compile verifier accepts. */
|
|
53
|
+
assetReadToken: string
|
|
54
|
+
/**
|
|
55
|
+
* directory uploaded `.swift` imports resolve under. defaults to the
|
|
56
|
+
* nearest `Package.swift` above each imported file — the swiftpm package
|
|
57
|
+
* that owns it — which is the compile unit, exactly like the local host.
|
|
58
|
+
* an app that keeps sources somewhere else names that package explicitly.
|
|
59
|
+
*/
|
|
60
|
+
packagePath?: string
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface RemoteProject {
|
|
64
|
+
packagePath: string
|
|
65
|
+
address: SwiftModuleAddress | null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// one address per package per process: a failed submit keeps serving the
|
|
69
|
+
// last published artifact (and warns) rather than failing the module, so
|
|
70
|
+
// the edit that fixes the error still has a graph to rebuild.
|
|
71
|
+
const projects = new Map<string, RemoteProject>()
|
|
72
|
+
|
|
73
|
+
function projectFor(packagePath: string): RemoteProject {
|
|
74
|
+
const existing = projects.get(packagePath)
|
|
75
|
+
if (existing) return existing
|
|
76
|
+
const created: RemoteProject = { packagePath, address: null }
|
|
77
|
+
projects.set(packagePath, created)
|
|
78
|
+
return created
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// every file under a directory, posix-relative, skipping what can never be
|
|
82
|
+
// a source, a manifest, or bundle content. a nested Package.swift is a
|
|
83
|
+
// separate package with its own compile: its files belong to that unit.
|
|
84
|
+
// `.build` holds tens of thousands of intermediates and no bundle content;
|
|
85
|
+
// walking it per import would tax every reload.
|
|
86
|
+
function packageFiles(root: string): string[] {
|
|
87
|
+
const found: string[] = []
|
|
88
|
+
const walk = (dir: string): void => {
|
|
89
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
90
|
+
if (
|
|
91
|
+
entry.name.startsWith('.') ||
|
|
92
|
+
entry.name === 'node_modules' ||
|
|
93
|
+
entry.name === '.build'
|
|
94
|
+
)
|
|
95
|
+
continue
|
|
96
|
+
const full = path.join(dir, entry.name)
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
if (full !== root && fs.existsSync(path.join(full, 'Package.swift'))) continue
|
|
99
|
+
walk(full)
|
|
100
|
+
} else {
|
|
101
|
+
found.push(path.relative(root, full).split(path.sep).join(path.posix.sep))
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
walk(root)
|
|
106
|
+
return found
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// the file map the service compiles: the package's `.swift` sources plus
|
|
110
|
+
// its root manifest, keyed package-relative. the manifest is partitioning
|
|
111
|
+
// input only — the service emits its own manifest that links every target
|
|
112
|
+
// against its SwiftUI module — so a standard local package travels as is.
|
|
113
|
+
function sourceFiles(packagePath: string): Record<string, string> {
|
|
114
|
+
const files: Record<string, string> = {}
|
|
115
|
+
for (const relative of packageFiles(packagePath)) {
|
|
116
|
+
if (!relative.endsWith('.swift')) continue
|
|
117
|
+
files[relative] = fs.readFileSync(
|
|
118
|
+
path.join(packagePath, ...relative.split('/')),
|
|
119
|
+
'utf8',
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
return files
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// the bundle pack and the photo uploads, through the shared enumeration
|
|
126
|
+
// both hosts pack with, so the collision winner here is the winner on the
|
|
127
|
+
// local host too. binary bytes straight from disk: this host is node.
|
|
128
|
+
function bundlePack(packagePath: string): {
|
|
129
|
+
resources: Record<string, string>
|
|
130
|
+
images: Record<string, { file: string; data: string }>
|
|
131
|
+
watched: string[]
|
|
132
|
+
} {
|
|
133
|
+
const relative = packageFiles(packagePath)
|
|
134
|
+
const disk = (file: string): string => path.join(packagePath, ...file.split('/'))
|
|
135
|
+
const resources: Record<string, string> = {}
|
|
136
|
+
const watched: string[] = []
|
|
137
|
+
for (const { base, file } of enumerateBundleJson(relative)) {
|
|
138
|
+
resources[base] = fs.readFileSync(disk(file)).toString('base64')
|
|
139
|
+
watched.push(file)
|
|
140
|
+
}
|
|
141
|
+
const images: Record<string, { file: string; data: string }> = {}
|
|
142
|
+
const byRelative = new Map(relative.map((file) => [file, disk(file)]))
|
|
143
|
+
for (const { name, file } of enumerateBundleImages(relative, (candidate) => {
|
|
144
|
+
const absolute = byRelative.get(candidate)
|
|
145
|
+
if (!absolute || path.basename(absolute) !== 'Contents.json') return null
|
|
146
|
+
try {
|
|
147
|
+
return fs.readFileSync(absolute, 'utf8')
|
|
148
|
+
} catch {
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
})) {
|
|
152
|
+
const absolute = byRelative.get(file)
|
|
153
|
+
if (!absolute) continue
|
|
154
|
+
images[name] = {
|
|
155
|
+
file: path.posix.basename(file),
|
|
156
|
+
data: fs.readFileSync(absolute).toString('base64'),
|
|
157
|
+
}
|
|
158
|
+
watched.push(file)
|
|
159
|
+
}
|
|
160
|
+
return { resources, images, watched }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// the swiftpm package that owns an imported file: the nearest directory at
|
|
164
|
+
// or above the file that holds a Package.swift. a `.swift` file with no
|
|
165
|
+
// package above it (a pod, an xcode target) is refused loud rather than
|
|
166
|
+
// compiled as the wrong unit.
|
|
167
|
+
function packageFor(file: string, stop?: string): string {
|
|
168
|
+
let dir = path.dirname(file)
|
|
169
|
+
for (;;) {
|
|
170
|
+
if (fs.existsSync(path.join(dir, 'Package.swift'))) return dir
|
|
171
|
+
if ((stop !== undefined && dir === stop) || path.dirname(dir) === dir) break
|
|
172
|
+
dir = path.dirname(dir)
|
|
173
|
+
}
|
|
174
|
+
throw new Error(
|
|
175
|
+
`no Package.swift above ${file}; a .swift import needs a swiftpm package like the one in packages/sootsim-swift/example`,
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function swiftRemotePlugin(options: SwiftRemotePluginOptions): Plugin {
|
|
180
|
+
const submit = httpSwiftCompileSubmit({
|
|
181
|
+
assetReadToken: options.assetReadToken,
|
|
182
|
+
endpoint: options.endpoint,
|
|
183
|
+
})
|
|
184
|
+
if (!submit) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
'swiftRemotePlugin needs an assetReadToken: a build without a compile credential cannot compile .swift sources',
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
name: 'rnx-swift-remote',
|
|
191
|
+
enforce: 'pre',
|
|
192
|
+
|
|
193
|
+
async resolveId(source, importer) {
|
|
194
|
+
if (source.startsWith(VIRTUAL_PREFIX)) return source
|
|
195
|
+
if (!source.endsWith('.swift')) return null
|
|
196
|
+
const resolved = await this.resolve(source, importer, { skipSelf: true })
|
|
197
|
+
if (!resolved) throw new Error(`cannot resolve swift source ${source}`)
|
|
198
|
+
return VIRTUAL_PREFIX + resolved.id
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
async load(id) {
|
|
202
|
+
if (!id.startsWith(VIRTUAL_PREFIX)) return null
|
|
203
|
+
const file = id.slice(VIRTUAL_PREFIX.length)
|
|
204
|
+
if (!fs.existsSync(file)) throw new Error(`swift source ${file} no longer exists`)
|
|
205
|
+
const packagePath = options.packagePath ?? packageFor(file)
|
|
206
|
+
const project = projectFor(packagePath)
|
|
207
|
+
const files = sourceFiles(packagePath)
|
|
208
|
+
const entry = path.relative(packagePath, file).split(path.sep).join(path.posix.sep)
|
|
209
|
+
for (const source of Object.keys(files)) {
|
|
210
|
+
this.addWatchFile(path.join(packagePath, ...source.split('/')))
|
|
211
|
+
}
|
|
212
|
+
const pack = bundlePack(packagePath)
|
|
213
|
+
for (const watched of pack.watched) {
|
|
214
|
+
this.addWatchFile(path.join(packagePath, ...watched.split('/')))
|
|
215
|
+
}
|
|
216
|
+
try {
|
|
217
|
+
project.address = await compileSwiftProject(
|
|
218
|
+
submit,
|
|
219
|
+
{ files, resources: pack.resources, images: pack.images, entry },
|
|
220
|
+
options.endpoint,
|
|
221
|
+
)
|
|
222
|
+
} catch (error) {
|
|
223
|
+
// a compile error must not fail this module: the graph would stop
|
|
224
|
+
// watching it and the fix would never rebuild. the last published
|
|
225
|
+
// artifact keeps rendering while the diagnostic is reported here.
|
|
226
|
+
if (!project.address) throw error
|
|
227
|
+
this.warn(error instanceof Error ? error.message : String(error))
|
|
228
|
+
}
|
|
229
|
+
const address = project.address
|
|
230
|
+
// every import mounts through the one entry: the artifact answers
|
|
231
|
+
// whether it is a view or an app root at runtime, so the transform
|
|
232
|
+
// never re-derives the conformance from source.
|
|
233
|
+
return [
|
|
234
|
+
`import { createSwiftEntry } from 'rnxsim/swift'`,
|
|
235
|
+
`export default createSwiftEntry({ url: ${JSON.stringify(address.url)}, hash: ${JSON.stringify(address.hash)}, rootId: ${JSON.stringify(entryRootId(packagePath, file))}, resources: ${JSON.stringify(address.imageUrls ?? {})}, bundleFiles: ${JSON.stringify(pack.resources)} })`,
|
|
236
|
+
'',
|
|
237
|
+
].join('\n')
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// helpers for serving the installed sootsim engine runtime from framework
|
|
2
|
+
// dev servers. the runtime is managed by `sootsim runtime install` and lives
|
|
3
|
+
// under ~/.rnx/runtimes/<version>.
|
|
4
|
+
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
import path from 'path'
|
|
7
|
+
import { fileURLToPath } from 'url'
|
|
8
|
+
import { isValidRuntimeVersion } from '@contrast/runtime-delivery'
|
|
9
|
+
import { readActiveRuntime, runtimeDir } from './home-paths.ts'
|
|
10
|
+
|
|
11
|
+
export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
|
|
12
|
+
'[rnx] no engine runtime installed. run `rnx runtime install`'
|
|
13
|
+
|
|
14
|
+
// --- dev-checkout mode boundary --------------------------------------------
|
|
15
|
+
//
|
|
16
|
+
// there is exactly one rule for which engine gets served: INSIDE the Contrast
|
|
17
|
+
// monorepo dev checkout the fresh dev-stack build (public/sootsim, kept current
|
|
18
|
+
// by `bun dev`'s watch:sootsim) IS the engine; OUTSIDE it (a published CLI in a
|
|
19
|
+
// user's project, or an isolated home) the installed runtime IS. this is a mode
|
|
20
|
+
// boundary, not a preference or a runtime fallback chain — the check is
|
|
21
|
+
// structural (the monorepo layout is present next to this module) and
|
|
22
|
+
// deterministic (the dev build's files exist). it exists because the daemon and
|
|
23
|
+
// the /__soot plugin otherwise serve ~/.rnx/runtimes/<active>, a PUBLISHED
|
|
24
|
+
// runtime that `bun dev` never refreshes, so local engine edits are invisible
|
|
25
|
+
// until a manual publish — the silent trap that cost a full night of
|
|
26
|
+
// already-fixed bugs re-reproducing.
|
|
27
|
+
//
|
|
28
|
+
// the isolated CI headless path (scripts/local-publish-runtime.sh) sets
|
|
29
|
+
// RNX_HOME to a dedicated dir precisely to validate the published-runtime
|
|
30
|
+
// code path; keying off that EXISTING isolation env keeps it (and packaged
|
|
31
|
+
// electron with a custom home) on the installed runtime.
|
|
32
|
+
let _monorepoRootCache: string | null | undefined
|
|
33
|
+
|
|
34
|
+
function findMonorepoRoot(): string | null {
|
|
35
|
+
if (_monorepoRootCache !== undefined) return _monorepoRootCache
|
|
36
|
+
let dir = path.dirname(fileURLToPath(import.meta.url))
|
|
37
|
+
// walk up until we find the Contrast monorepo: it has packages/sootsim-engine and
|
|
38
|
+
// a public/sootsim dev-build output slot. bounded by the filesystem root.
|
|
39
|
+
for (let i = 0; i < 12 && dir !== path.dirname(dir); i++) {
|
|
40
|
+
if (
|
|
41
|
+
fs.existsSync(path.join(dir, 'packages/sootsim-engine/package.json')) &&
|
|
42
|
+
fs.existsSync(path.join(dir, 'public/sootsim'))
|
|
43
|
+
) {
|
|
44
|
+
_monorepoRootCache = dir
|
|
45
|
+
return dir
|
|
46
|
+
}
|
|
47
|
+
dir = path.dirname(dir)
|
|
48
|
+
}
|
|
49
|
+
_monorepoRootCache = null
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** the fresh dev-stack engine build to serve when running inside the Contrast
|
|
54
|
+
* monorepo dev checkout, or null when the installed runtime should serve
|
|
55
|
+
* (published CLI, packaged electron, or an isolated RNX_HOME). */
|
|
56
|
+
export function resolveDevCheckoutRuntimeRoot(): string | null {
|
|
57
|
+
// an explicit home that has a STAGED/active runtime is the isolation the
|
|
58
|
+
// CI/local-publish path sets up to validate the installed-runtime code path —
|
|
59
|
+
// honor that staged runtime. an explicit home with NO active runtime is just
|
|
60
|
+
// an isolated dev daemon (own lockfile, doesn't clobber ~/.rnx), which
|
|
61
|
+
// still wants the dev build; and the default home (RNX_HOME unset) always
|
|
62
|
+
// does. keying on "explicit home + a runtime deliberately staged there" keeps
|
|
63
|
+
// it structural, not a bespoke toggle.
|
|
64
|
+
if (process.env.RNX_HOME && readActiveRuntime()) return null
|
|
65
|
+
const root = findMonorepoRoot()
|
|
66
|
+
if (!root) return null
|
|
67
|
+
const devBuild = path.join(root, 'public/sootsim')
|
|
68
|
+
// require the runtime shape the installed runtime also has, so serving it is
|
|
69
|
+
// a drop-in (index.html shell + engine/ + engine-tenant/worker.js tenant).
|
|
70
|
+
if (
|
|
71
|
+
fs.existsSync(path.join(devBuild, 'index.html')) &&
|
|
72
|
+
fs.existsSync(path.join(devBuild, 'engine/index.html')) &&
|
|
73
|
+
fs.existsSync(path.join(devBuild, 'engine-tenant/worker.js'))
|
|
74
|
+
) {
|
|
75
|
+
return devBuild
|
|
76
|
+
}
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const MIME_TYPES: Record<string, string> = {
|
|
81
|
+
'.js': 'application/javascript',
|
|
82
|
+
'.mjs': 'application/javascript',
|
|
83
|
+
'.css': 'text/css',
|
|
84
|
+
'.html': 'text/html',
|
|
85
|
+
'.wasm': 'application/wasm',
|
|
86
|
+
'.json': 'application/json',
|
|
87
|
+
'.jpg': 'image/jpeg',
|
|
88
|
+
'.jpeg': 'image/jpeg',
|
|
89
|
+
'.png': 'image/png',
|
|
90
|
+
'.svg': 'image/svg+xml',
|
|
91
|
+
'.webp': 'image/webp',
|
|
92
|
+
'.glb': 'model/gltf-binary',
|
|
93
|
+
'.ttf': 'font/ttf',
|
|
94
|
+
'.otf': 'font/otf',
|
|
95
|
+
'.woff': 'font/woff',
|
|
96
|
+
'.woff2': 'font/woff2',
|
|
97
|
+
'.mp3': 'audio/mpeg',
|
|
98
|
+
'.wav': 'audio/wav',
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const ROOT_RUNTIME_PATHS = [
|
|
102
|
+
'/assets/',
|
|
103
|
+
'/engine/',
|
|
104
|
+
'/engine-tenant/',
|
|
105
|
+
'/photos/',
|
|
106
|
+
'/three-mode/',
|
|
107
|
+
'/fonts/',
|
|
108
|
+
'/icons/',
|
|
109
|
+
'/sounds/',
|
|
110
|
+
'/spike/',
|
|
111
|
+
'/test-wallpaper.jpg',
|
|
112
|
+
'/preview-sw.js',
|
|
113
|
+
'/sootsim.svg',
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
export function resolveActiveRuntimeRoot(): string | null {
|
|
117
|
+
// mode boundary: inside the dev checkout the dev-stack build is the engine.
|
|
118
|
+
const devRoot = resolveDevCheckoutRuntimeRoot()
|
|
119
|
+
if (devRoot) return devRoot
|
|
120
|
+
const active = readActiveRuntime()
|
|
121
|
+
if (!active) return null
|
|
122
|
+
const dir = runtimeDir(active)
|
|
123
|
+
if (!fs.existsSync(path.join(dir, 'index.html'))) return null
|
|
124
|
+
return dir
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const RUNTIME_VERSION_HOSTNAME_SUFFIX = '.runtime.localhost'
|
|
128
|
+
|
|
129
|
+
export function runtimeVersionHostname(version: string): string {
|
|
130
|
+
const trimmed = version.trim()
|
|
131
|
+
if (!isValidRuntimeVersion(trimmed)) {
|
|
132
|
+
throw new Error(`invalid rnx runtime version: ${version}`)
|
|
133
|
+
}
|
|
134
|
+
const encoded = Buffer.from(trimmed, 'utf8').toString('hex')
|
|
135
|
+
const labels = encoded.match(/.{1,60}/g)
|
|
136
|
+
if (!labels) throw new Error(`invalid rnx runtime version: ${version}`)
|
|
137
|
+
return `${labels.join('.')}${RUNTIME_VERSION_HOSTNAME_SUFFIX}`
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function isRuntimeVersionHostname(hostname: string): boolean {
|
|
141
|
+
return hostname.toLowerCase().endsWith(RUNTIME_VERSION_HOSTNAME_SUFFIX)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function runtimeVersionFromHostname(hostname: string): string | null {
|
|
145
|
+
if (!isRuntimeVersionHostname(hostname)) return null
|
|
146
|
+
const labels = hostname.slice(0, -RUNTIME_VERSION_HOSTNAME_SUFFIX.length).split('.')
|
|
147
|
+
if (labels.length === 0 || labels.some((label) => !/^[0-9a-f]{1,60}$/i.test(label))) {
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
const encoded = labels.join('')
|
|
151
|
+
if (encoded.length % 2 !== 0) return null
|
|
152
|
+
try {
|
|
153
|
+
const version = Buffer.from(encoded, 'hex').toString('utf8')
|
|
154
|
+
return isValidRuntimeVersion(version) ? version : null
|
|
155
|
+
} catch {
|
|
156
|
+
return null
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function resolveInstalledRuntimeRoot(version: string): string | null {
|
|
161
|
+
const dir = runtimeDir(version)
|
|
162
|
+
if (!fs.existsSync(path.join(dir, 'index.html'))) return null
|
|
163
|
+
return dir
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** select one immutable installed runtime for a version-owned origin. ordinary
|
|
167
|
+
* localhost requests follow the active runtime; a malformed or unavailable
|
|
168
|
+
* version origin fails closed instead of borrowing active assets. */
|
|
169
|
+
export function resolveRuntimeRootForHostname(hostname: string): string | null {
|
|
170
|
+
if (!isRuntimeVersionHostname(hostname)) return resolveActiveRuntimeRoot()
|
|
171
|
+
const version = runtimeVersionFromHostname(hostname)
|
|
172
|
+
return version ? resolveInstalledRuntimeRoot(version) : null
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function resolveRuntimeRootForRequestHost(
|
|
176
|
+
host: string | undefined,
|
|
177
|
+
): string | null {
|
|
178
|
+
if (!host) return resolveActiveRuntimeRoot()
|
|
179
|
+
try {
|
|
180
|
+
return resolveRuntimeRootForHostname(new URL(`http://${host}`).hostname)
|
|
181
|
+
} catch {
|
|
182
|
+
return null
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function isRootRuntimeAssetPath(pathname: string): boolean {
|
|
187
|
+
return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function resolveRuntimeFilePath(
|
|
191
|
+
runtimeRoot: string,
|
|
192
|
+
pathname: string,
|
|
193
|
+
): string | null {
|
|
194
|
+
if (!pathname.startsWith('/')) return null
|
|
195
|
+
if (pathname.includes('\0') || pathname.includes('\\')) return null
|
|
196
|
+
for (const segment of pathname.split('/')) {
|
|
197
|
+
if (segment === '..') return null
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const fullPath = path.resolve(runtimeRoot, pathname.replace(/^\/+/, ''))
|
|
201
|
+
const rootWithSep = runtimeRoot.endsWith(path.sep)
|
|
202
|
+
? runtimeRoot
|
|
203
|
+
: runtimeRoot + path.sep
|
|
204
|
+
if (!fullPath.startsWith(rootWithSep) && fullPath !== runtimeRoot) return null
|
|
205
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) return null
|
|
206
|
+
return fullPath
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function serveRuntimeFile(req: any, res: any, fullPath: string) {
|
|
210
|
+
const ext = path.extname(fullPath)
|
|
211
|
+
const stat = fs.statSync(fullPath)
|
|
212
|
+
const lastModified = stat.mtime.toUTCString()
|
|
213
|
+
res.setHeader('content-type', MIME_TYPES[ext] || 'application/octet-stream')
|
|
214
|
+
if (/^canvaskit-graphite-pipelines-[0-9a-f]{8}\.bin$/.test(path.basename(fullPath))) {
|
|
215
|
+
res.setHeader('cache-control', 'public, max-age=31536000, immutable')
|
|
216
|
+
fs.createReadStream(fullPath).pipe(res)
|
|
217
|
+
return
|
|
218
|
+
}
|
|
219
|
+
// these urls are STABLE across dev rebuilds and runtime version switches
|
|
220
|
+
// (worker.js, canvaskit-graphite.wasm, the html shells are unhashed), so the
|
|
221
|
+
// browser must revalidate every load — the old year-long immutable here made every
|
|
222
|
+
// new tab run a stale cached engine-tenant worker.js against a fresh engine,
|
|
223
|
+
// which also broke `do reload` ("bridge never reconnected"). no-cache +
|
|
224
|
+
// last-modified keeps repeat loads cheap 304s on localhost.
|
|
225
|
+
res.setHeader('cache-control', 'no-cache')
|
|
226
|
+
res.setHeader('last-modified', lastModified)
|
|
227
|
+
if (req?.headers?.['if-modified-since'] === lastModified) {
|
|
228
|
+
res.statusCode = 304
|
|
229
|
+
res.end()
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
fs.createReadStream(fullPath).pipe(res)
|
|
233
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// rnx's binding of the shared runtime-delivery machinery.
|
|
2
|
+
//
|
|
3
|
+
// the machinery itself (hosted manifest, sha256-verified tarballs, channels,
|
|
4
|
+
// channel resolution, and auto-update) lives in @contrast/runtime-delivery and
|
|
5
|
+
// knows nothing about rnx. this file supplies rnx's identity, public env
|
|
6
|
+
// overrides, and the contrast.dev/runtimes hosting layout.
|
|
7
|
+
//
|
|
8
|
+
// @contrast/runtime-delivery is an internal workspace package that is never
|
|
9
|
+
// published, so both the CLI bundle and dist-lib inline it (see the `alias`
|
|
10
|
+
// entry in scripts/build-cli.ts). pack-smoke fails the build if it ever leaks
|
|
11
|
+
// out as a bare require.
|
|
12
|
+
|
|
13
|
+
import { createRuntimeDelivery, type RuntimeProduct } from '@contrast/runtime-delivery'
|
|
14
|
+
import {
|
|
15
|
+
cacheDir,
|
|
16
|
+
configFilePath,
|
|
17
|
+
ensureRnxHome,
|
|
18
|
+
readActiveRuntime,
|
|
19
|
+
runtimeDir,
|
|
20
|
+
runtimesDir,
|
|
21
|
+
writeActiveRuntime,
|
|
22
|
+
} from './home-paths.ts'
|
|
23
|
+
import { rnxPublicBrand } from './public-brand.ts'
|
|
24
|
+
|
|
25
|
+
export const DEFAULT_RUNTIME_CDN_ORIGIN = 'https://contrast.dev'
|
|
26
|
+
|
|
27
|
+
/** the official curated changelog and the machine-readable release feed. the
|
|
28
|
+
* rnx site is static, so its /api traffic is owned by contrast.dev. */
|
|
29
|
+
export const RNX_CHANGELOG_URL = `${rnxPublicBrand.origin}/changelog`
|
|
30
|
+
export const RNX_CHANGELOG_API_URL = 'https://contrast.dev/api/changelog/sootsim'
|
|
31
|
+
|
|
32
|
+
export const RNX_RUNTIME_PRODUCT: RuntimeProduct = {
|
|
33
|
+
id: 'rnx',
|
|
34
|
+
defaultCdnOrigin: DEFAULT_RUNTIME_CDN_ORIGIN,
|
|
35
|
+
manifestPath: '/runtimes/manifest.json',
|
|
36
|
+
tarballPath: (version) => `/runtimes/sootsim-runtime-${version}.tar.gz`,
|
|
37
|
+
// the engine runtime is a static SPA: shell at the root, engine/ and
|
|
38
|
+
// engine-tenant/ beneath it. index.html is what the daemon and the
|
|
39
|
+
// vite/metro plugins serve, so its absence means the tarball is not one.
|
|
40
|
+
installedMarker: 'index.html',
|
|
41
|
+
env: {
|
|
42
|
+
cdnOrigin: 'RNX_CDN_ORIGIN',
|
|
43
|
+
channel: 'RNX_RUNTIME_CHANNEL',
|
|
44
|
+
},
|
|
45
|
+
ensureHome: ensureRnxHome,
|
|
46
|
+
configFile: configFilePath,
|
|
47
|
+
runtimesDir,
|
|
48
|
+
runtimeDir,
|
|
49
|
+
cacheDir,
|
|
50
|
+
readActiveRuntime,
|
|
51
|
+
writeActiveRuntime,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const rnxRuntime = createRuntimeDelivery(RNX_RUNTIME_PRODUCT)
|