peachsim 0.1.2210 → 0.1.2215
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/command-registry.ts +1 -0
- package/cli/commands/inspect/settling.ts +1 -1
- package/cli/commands/map.ts +576 -0
- package/cli/commands/runtime.ts +1 -1
- package/cli/main.ts +6 -0
- package/cli/outbound-endpoints.ts +11 -0
- package/dist-lib/agent-daemon-client.cjs +1 -1
- package/dist-lib/agent-events.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 +1 -1
- package/dist-lib/bridge-contract-input.mjs +1 -1
- package/dist-lib/bridge-contract.cjs +1 -1
- package/dist-lib/bridge-contract.mjs +1 -1
- package/dist-lib/bundle-channel.cjs +31 -13
- package/dist-lib/capture-contract.cjs +1 -1
- package/dist-lib/capture-contract.mjs +1 -1
- package/dist-lib/cloud-contract.cjs +1 -1
- package/dist-lib/cloud-contract.mjs +1 -1
- package/dist-lib/cloud-test-args.cjs +1 -1
- package/dist-lib/cloud-test-args.mjs +1 -1
- package/dist-lib/cloud.cjs +1 -1
- package/dist-lib/cloud.mjs +1 -1
- package/dist-lib/config.cjs +1 -1
- package/dist-lib/config.mjs +1 -1
- package/dist-lib/detox/index.cjs +6 -3
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/expo-swift-bundle.cjs +29 -11
- package/dist-lib/expo-swift-metadata.cjs +1 -1
- package/dist-lib/expo-swift-metadata.mjs +1 -1
- package/dist-lib/home-paths.cjs +1 -1
- package/dist-lib/host/bridge-host.cjs +1019 -819
- package/dist-lib/host/fetch-proxy-handler.cjs +8 -5
- 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 +1956 -752
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/jump-to-source-native.cjs +1 -1
- package/dist-lib/kotlin-host.cjs +1 -1
- package/dist-lib/menu.cjs +1 -1
- package/dist-lib/menu.mjs +1 -1
- package/dist-lib/metro-fingerprint-registry.cjs +1 -1
- package/dist-lib/metro-fingerprint-registry.mjs +1 -1
- package/dist-lib/metro-production-bundle.cjs +1 -1
- package/dist-lib/metro-production-bundle.mjs +1 -1
- package/dist-lib/metro.cjs +1796 -593
- package/dist-lib/native-app-identity-project.cjs +6 -3
- package/dist-lib/native-app-identity-project.mjs +6 -3
- package/dist-lib/native-app-identity.cjs +1 -1
- package/dist-lib/native-app-identity.mjs +1 -1
- package/dist-lib/native-firebase-metadata.cjs +1 -1
- package/dist-lib/native-firebase-metadata.mjs +1 -1
- package/dist-lib/pdfium-raster.cjs +1 -1
- package/dist-lib/pdfium-raster.mjs +1 -1
- package/dist-lib/plist.cjs +1 -1
- package/dist-lib/plist.mjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/public-brand.cjs +1 -1
- package/dist-lib/public-origins-lib-esm.json +25 -24
- package/dist-lib/public-origins-lib.json +73 -53
- package/dist-lib/react-native-host-modules.cjs +1 -1
- package/dist-lib/react-native-host-modules.mjs +1 -1
- package/dist-lib/reap-browser.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +1 -1
- package/dist-lib/sdk.cjs +2 -2
- package/dist-lib/sdk.mjs +2 -2
- package/dist-lib/skills.cjs +1555 -1297
- package/dist-lib/swift-compile.cjs +2 -2
- package/dist-lib/swift-compile.mjs +2 -2
- package/dist-lib/swift-package.cjs +1 -1
- package/dist-lib/swift-package.mjs +1 -1
- package/dist-lib/swift-submit.cjs +1 -1
- package/dist-lib/swift-submit.mjs +1 -1
- package/dist-lib/swift-toolchain.cjs +1 -1
- package/dist-lib/swift.cjs +1 -1
- package/dist-lib/vite-swift.cjs +21 -9
- package/dist-lib/vite.cjs +1520 -303
- package/dist-lib/worklet-executable.cjs +1 -1
- package/dist-lib/worklet-executable.mjs +1 -1
- package/dist-types/packages/peach-engine/src/engine/font-config.d.ts +1 -1
- package/dist-types/packages/peach-engine/src/engine/native-layout.d.ts +4 -0
- package/dist-types/packages/peach-engine/src/engine/node.d.ts +1 -0
- package/dist-types/packages/peach-engine/src/engine/scene-table.d.ts +4 -0
- package/dist-types/packages/peach-engine/src/engine/scroll.d.ts +3 -0
- package/dist-types/packages/peach-engine/src/render-worker/shell-protocol.d.ts +42 -0
- package/kotlin/sdk.tar.gz +0 -0
- package/package.json +1 -1
- package/runtime-companion.json +1 -1
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/CoreData.apinotes +35 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/CoreData.h +8 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSAttributeDescription.h +30 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSEntityDescription.h +14 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSExpressionDescription.h +10 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSFetchIndexDescription.h +13 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSFetchIndexElementDescription.h +18 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSManagedObject.h +11 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSManagedObjectContext.h +19 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSManagedObjectID.h +2 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSManagedObjectModel.h +3 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSMergePolicy.h +17 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSPersistentStore.h +8 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSPersistentStoreCoordinator.h +6 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSPropertyDescription.h +13 -0
- package/sdk/ios/System/Library/Frameworks/CoreData.framework/Headers/NSRelationshipDescription.h +21 -0
- package/sdk/ios/System/Library/Frameworks/UIKit.framework/Headers/UIImage.h +1 -10
- package/sdk/ios/System/Library/Frameworks/UIKitTypes.framework/Headers/UIKitTypes.h +11 -0
- package/sdk/ios/wasm/include/fcntl.h +3 -0
- package/skills/contrast/SKILL.md +1 -0
- package/src/home-paths.ts +1 -1
- package/src/host/bridge-host.ts +101 -97
- package/src/metro-plugin.ts +4 -1
- package/src/react-native-coverage/native-source-ranges-file.ts +1 -1
- package/src/runtime-assets.ts +0 -1
- package/src/runtime-delivery.ts +1 -1
- package/src/runtime-packs.ts +212 -0
- package/src/swift-package-manifest.ts +7 -2
- package/src/swift-shipped-binaries.ts +1 -1
- package/src/swift-standin-config.ts +4 -2
- package/src/swift-standin-emit.ts +1 -1
- package/src/swift-standins.ts +10 -3
- package/src/vite-plugin-one.ts +4 -1
package/src/host/bridge-host.ts
CHANGED
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
sendFileWithRanges,
|
|
56
56
|
} from '../runtime-assets.ts'
|
|
57
57
|
import { rnxRuntime } from '../runtime-delivery.ts'
|
|
58
|
+
import { ensureRuntimePackFor } from '../runtime-packs.ts'
|
|
58
59
|
import { handleTurboRequest, TURBO_ROUTE } from '../turbo-bundle.ts'
|
|
59
60
|
import { AgentHost, type AgentHostOptions } from './agent-host.ts'
|
|
60
61
|
import { serveAppPackageResource } from './app-package-handler'
|
|
@@ -1920,116 +1921,119 @@ export class SootSimBridgeHost {
|
|
|
1920
1921
|
// inside baseDir that point outside — without it, an attacker who can
|
|
1921
1922
|
// drop a symlink in the runtime dir gets arbitrary-read through the
|
|
1922
1923
|
// daemon. realpath also collapses any remaining normalizable segments.
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
?
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1924
|
+
// a pack-owned prefix downloads on first request before the file resolves
|
|
1925
|
+
void ensureRuntimePackFor(baseDir, rel).then(() =>
|
|
1926
|
+
fs.realpath(resolved, (realErr, realResolved) => {
|
|
1927
|
+
const servePath = realErr ? resolved : realResolved
|
|
1928
|
+
const servePathWithSep = servePath.endsWith(path.sep)
|
|
1929
|
+
? servePath
|
|
1930
|
+
: servePath + path.sep
|
|
1931
|
+
if (!realErr) {
|
|
1932
|
+
const realBaseWithSep = (() => {
|
|
1933
|
+
try {
|
|
1934
|
+
const rb = fs.realpathSync(baseDir)
|
|
1935
|
+
return rb.endsWith(path.sep) ? rb : rb + path.sep
|
|
1936
|
+
} catch {
|
|
1937
|
+
return baseWithSep
|
|
1938
|
+
}
|
|
1939
|
+
})()
|
|
1940
|
+
if (
|
|
1941
|
+
!servePathWithSep.startsWith(realBaseWithSep) &&
|
|
1942
|
+
servePath + path.sep !== realBaseWithSep
|
|
1943
|
+
) {
|
|
1944
|
+
res.writeHead(403)
|
|
1945
|
+
res.end('forbidden')
|
|
1946
|
+
return
|
|
1935
1947
|
}
|
|
1936
|
-
})()
|
|
1937
|
-
if (
|
|
1938
|
-
!servePathWithSep.startsWith(realBaseWithSep) &&
|
|
1939
|
-
servePath + path.sep !== realBaseWithSep
|
|
1940
|
-
) {
|
|
1941
|
-
res.writeHead(403)
|
|
1942
|
-
res.end('forbidden')
|
|
1943
|
-
return
|
|
1944
1948
|
}
|
|
1945
|
-
}
|
|
1946
1949
|
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
res.writeHead(404)
|
|
1955
|
-
res.end('not found')
|
|
1956
|
-
return
|
|
1957
|
-
}
|
|
1958
|
-
// never SPA-fallback internal endpoint namespaces — those are
|
|
1959
|
-
// expected to return JSON (or a real 404) and the engine calls
|
|
1960
|
-
// .json() on the response. without this, a tenant-worker fetch
|
|
1961
|
-
// to an unimplemented /__foo or /api/foo gets a 200 + index.html
|
|
1962
|
-
// and explodes with "Unexpected token '<', '<!doctype'... is not
|
|
1963
|
-
// valid JSON". /__server-scan is the canonical case (see the
|
|
1964
|
-
// handler above).
|
|
1965
|
-
if (rel.startsWith('/__') || rel.startsWith('/api/') || rel === '/api') {
|
|
1966
|
-
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' })
|
|
1967
|
-
res.end('not found')
|
|
1968
|
-
return
|
|
1969
|
-
}
|
|
1970
|
-
const indexPath = path.join(baseDir, 'index.html')
|
|
1971
|
-
fs.readFile(indexPath, (err2, data) => {
|
|
1972
|
-
if (err2) {
|
|
1950
|
+
fs.stat(servePath, (err, stats) => {
|
|
1951
|
+
if (err || !stats?.isFile()) {
|
|
1952
|
+
// SPA fallback — but only for extensionless paths. a 404 on
|
|
1953
|
+
// /main.a1b2c3.js should 404, not return index.html as js,
|
|
1954
|
+
// which the sim then chokes on.
|
|
1955
|
+
const ext = path.extname(rel).toLowerCase()
|
|
1956
|
+
if (ext && ext !== '.html') {
|
|
1973
1957
|
res.writeHead(404)
|
|
1974
1958
|
res.end('not found')
|
|
1975
1959
|
return
|
|
1976
1960
|
}
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1961
|
+
// never SPA-fallback internal endpoint namespaces — those are
|
|
1962
|
+
// expected to return JSON (or a real 404) and the engine calls
|
|
1963
|
+
// .json() on the response. without this, a tenant-worker fetch
|
|
1964
|
+
// to an unimplemented /__foo or /api/foo gets a 200 + index.html
|
|
1965
|
+
// and explodes with "Unexpected token '<', '<!doctype'... is not
|
|
1966
|
+
// valid JSON". /__server-scan is the canonical case (see the
|
|
1967
|
+
// handler above).
|
|
1968
|
+
if (rel.startsWith('/__') || rel.startsWith('/api/') || rel === '/api') {
|
|
1969
|
+
res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' })
|
|
1970
|
+
res.end('not found')
|
|
1983
1971
|
return
|
|
1984
1972
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
res.writeHead(200, {
|
|
1998
|
-
'Content-Type': contentType,
|
|
1999
|
-
'Cache-Control': 'no-store',
|
|
2000
|
-
// native file documents have an opaque origin when reading public fonts.
|
|
2001
|
-
...(publicEngineFont ? { 'Access-Control-Allow-Origin': '*' } : {}),
|
|
2002
|
-
})
|
|
2003
|
-
if (method === 'HEAD') {
|
|
2004
|
-
res.end()
|
|
2005
|
-
return
|
|
2006
|
-
}
|
|
2007
|
-
// html responses inline the daemon-injected `__sootsimSharedConfig`
|
|
2008
|
-
// global so the engine's settingsStore initializes from disk on
|
|
2009
|
-
// boot. for everything else stream the file straight through.
|
|
2010
|
-
if (ext === '.html') {
|
|
2011
|
-
fs.readFile(servePath, (readErr, data) => {
|
|
2012
|
-
if (readErr) {
|
|
2013
|
-
try {
|
|
1973
|
+
const indexPath = path.join(baseDir, 'index.html')
|
|
1974
|
+
fs.readFile(indexPath, (err2, data) => {
|
|
1975
|
+
if (err2) {
|
|
1976
|
+
res.writeHead(404)
|
|
1977
|
+
res.end('not found')
|
|
1978
|
+
return
|
|
1979
|
+
}
|
|
1980
|
+
res.writeHead(200, {
|
|
1981
|
+
'Content-Type': 'text/html; charset=utf-8',
|
|
1982
|
+
'Cache-Control': 'no-store',
|
|
1983
|
+
})
|
|
1984
|
+
if (method === 'HEAD') {
|
|
2014
1985
|
res.end()
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
)
|
|
1986
|
+
return
|
|
1987
|
+
}
|
|
1988
|
+
res.end(
|
|
1989
|
+
injectSharedConfigIntoHtml(data, this.effectivePort, this.contrastOrigin),
|
|
1990
|
+
)
|
|
1991
|
+
})
|
|
1992
|
+
return
|
|
1993
|
+
}
|
|
1994
|
+
const ext = path.extname(servePath).toLowerCase()
|
|
1995
|
+
const contentType = HTTP_MIME_TYPES[ext] || 'application/octet-stream'
|
|
1996
|
+
// no-store across the board: the runtime is served from a versioned
|
|
1997
|
+
// directory but the HTTP path doesn't include the version, so the
|
|
1998
|
+
// same URL serves different content after a runtime swap. caching
|
|
1999
|
+
// any asset means a stale-bundle reload after runtime:changed.
|
|
2000
|
+
res.writeHead(200, {
|
|
2001
|
+
'Content-Type': contentType,
|
|
2002
|
+
'Cache-Control': 'no-store',
|
|
2003
|
+
// native file documents have an opaque origin when reading public fonts.
|
|
2004
|
+
...(publicEngineFont ? { 'Access-Control-Allow-Origin': '*' } : {}),
|
|
2021
2005
|
})
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2024
|
-
const stream = fs.createReadStream(servePath)
|
|
2025
|
-
stream.pipe(res)
|
|
2026
|
-
stream.on('error', () => {
|
|
2027
|
-
try {
|
|
2006
|
+
if (method === 'HEAD') {
|
|
2028
2007
|
res.end()
|
|
2029
|
-
|
|
2008
|
+
return
|
|
2009
|
+
}
|
|
2010
|
+
// html responses inline the daemon-injected `__sootsimSharedConfig`
|
|
2011
|
+
// global so the engine's settingsStore initializes from disk on
|
|
2012
|
+
// boot. for everything else stream the file straight through.
|
|
2013
|
+
if (ext === '.html') {
|
|
2014
|
+
fs.readFile(servePath, (readErr, data) => {
|
|
2015
|
+
if (readErr) {
|
|
2016
|
+
try {
|
|
2017
|
+
res.end()
|
|
2018
|
+
} catch {}
|
|
2019
|
+
return
|
|
2020
|
+
}
|
|
2021
|
+
res.end(
|
|
2022
|
+
injectSharedConfigIntoHtml(data, this.effectivePort, this.contrastOrigin),
|
|
2023
|
+
)
|
|
2024
|
+
})
|
|
2025
|
+
return
|
|
2026
|
+
}
|
|
2027
|
+
const stream = fs.createReadStream(servePath)
|
|
2028
|
+
stream.pipe(res)
|
|
2029
|
+
stream.on('error', () => {
|
|
2030
|
+
try {
|
|
2031
|
+
res.end()
|
|
2032
|
+
} catch {}
|
|
2033
|
+
})
|
|
2030
2034
|
})
|
|
2031
|
-
})
|
|
2032
|
-
|
|
2035
|
+
}),
|
|
2036
|
+
)
|
|
2033
2037
|
}
|
|
2034
2038
|
|
|
2035
2039
|
private sweepIdleCliClients() {
|
package/src/metro-plugin.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
serveRuntimeFile,
|
|
52
52
|
SOOTSIM_RUNTIME_MISSING_MESSAGE,
|
|
53
53
|
} from './runtime-assets'
|
|
54
|
+
import { ensureRuntimePackFor } from './runtime-packs'
|
|
54
55
|
import {
|
|
55
56
|
getRuntimePlatformAdapter,
|
|
56
57
|
isRuntimePlatform,
|
|
@@ -451,7 +452,7 @@ export function withPeach<T extends Record<string, unknown>>(
|
|
|
451
452
|
|
|
452
453
|
const server = connect()
|
|
453
454
|
|
|
454
|
-
server.use((req: any, res: any, next: any) => {
|
|
455
|
+
server.use(async (req: any, res: any, next: any) => {
|
|
455
456
|
if (isReplacementModuleRequestUrl(req.url)) {
|
|
456
457
|
void handleReplacementModuleRequest(req, res, {
|
|
457
458
|
servedRoots: [cfgProjectRoot, ...cfgWatchFolders],
|
|
@@ -501,6 +502,8 @@ export function withPeach<T extends Record<string, unknown>>(
|
|
|
501
502
|
return
|
|
502
503
|
}
|
|
503
504
|
|
|
505
|
+
await ensureRuntimePackFor(runtimeRoot, pathname)
|
|
506
|
+
|
|
504
507
|
// root-relative runtime assets emitted by the shell and engine vite
|
|
505
508
|
// builds. if a user app has its own /assets/foo request, we only
|
|
506
509
|
// intercept when that exact file exists in the runtime; otherwise
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
-
import rangesData from '../../../peach-compat/src/generated/native-source-ranges.json'
|
|
2
|
+
import rangesData from '../../../peach-compat/src/generated/native-source-ranges.json' with { type: 'json' }
|
|
3
3
|
import type { NativeSourceRangesFile } from '../../../peach-compat/src/native-source-ranges-types'
|
|
4
4
|
|
|
5
5
|
const run = z.object({
|
package/src/runtime-assets.ts
CHANGED
package/src/runtime-delivery.ts
CHANGED
|
@@ -33,7 +33,7 @@ export const RNX_RUNTIME_PRODUCT: RuntimeProduct = {
|
|
|
33
33
|
id: 'peach',
|
|
34
34
|
defaultCdnOrigin: DEFAULT_RUNTIME_CDN_ORIGIN,
|
|
35
35
|
manifestPath: '/runtimes/manifest.json',
|
|
36
|
-
tarballPath: (version) => `/runtimes/sootsim-runtime-${version}.tar.
|
|
36
|
+
tarballPath: (version) => `/runtimes/sootsim-runtime-${version}.tar.zst`,
|
|
37
37
|
// the engine runtime is a static SPA: shell at the root, engine/ and
|
|
38
38
|
// engine-tenant/ beneath it. index.html is what the daemon and the
|
|
39
39
|
// vite/metro plugins serve, so its absence means the tarball is not one.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { setTimeout } from 'node:timers'
|
|
4
|
+
import {
|
|
5
|
+
installVerifiedTarball,
|
|
6
|
+
RUNTIME_MERGE_PACK_DIR,
|
|
7
|
+
RUNTIME_MERGE_PACK_INSTALL,
|
|
8
|
+
RUNTIME_MERGE_PACK_MERGED,
|
|
9
|
+
RUNTIME_PACK_MARKER,
|
|
10
|
+
RUNTIME_PACK_PREFIXES,
|
|
11
|
+
RUNTIME_PACKS_FILE,
|
|
12
|
+
type RuntimePacks,
|
|
13
|
+
} from '@contrast/runtime-delivery'
|
|
14
|
+
import { rnxRuntime } from './runtime-delivery.ts'
|
|
15
|
+
|
|
16
|
+
// pieces only some apps reach ship beside the runtime archive, not in it. the
|
|
17
|
+
// archive carries `packs.json` naming each; the first request under a pack's
|
|
18
|
+
// prefix downloads it, checks its digest and lands it by rename, exactly as the
|
|
19
|
+
// archive itself installs. the desktop bundle keeps every prefix in place.
|
|
20
|
+
const inflight = new Map<string, Promise<void>>()
|
|
21
|
+
// an installed version's packs.json never changes
|
|
22
|
+
const packsByRoot = new Map<string, RuntimePacks | null>()
|
|
23
|
+
// listed pack files by basename, each with its runtime-relative path and pack
|
|
24
|
+
const listedByRoot = new Map<string, Map<string, { file: string; pack: string }[]>>()
|
|
25
|
+
|
|
26
|
+
function listedFiles(runtimeRoot: string) {
|
|
27
|
+
let listed = listedByRoot.get(runtimeRoot)
|
|
28
|
+
if (listed) return listed
|
|
29
|
+
listed = new Map()
|
|
30
|
+
for (const [pack, entry] of Object.entries(readPacks(runtimeRoot) ?? {}))
|
|
31
|
+
for (const file of entry.files ?? []) {
|
|
32
|
+
const name = path.posix.basename(file)
|
|
33
|
+
listed.set(name, [...(listed.get(name) ?? []), { file, pack }])
|
|
34
|
+
}
|
|
35
|
+
listedByRoot.set(runtimeRoot, listed)
|
|
36
|
+
return listed
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readPacks(runtimeRoot: string): RuntimePacks | null {
|
|
40
|
+
const cached = packsByRoot.get(runtimeRoot)
|
|
41
|
+
if (cached !== undefined) return cached
|
|
42
|
+
const file = path.join(runtimeRoot, RUNTIME_PACKS_FILE)
|
|
43
|
+
if (!fs.existsSync(file)) {
|
|
44
|
+
packsByRoot.set(runtimeRoot, null)
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
48
|
+
const packs: RuntimePacks = {}
|
|
49
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
50
|
+
throw new Error(`${file} is not an object`)
|
|
51
|
+
for (const [prefix, entry] of Object.entries(parsed)) {
|
|
52
|
+
if (typeof entry?.file !== 'string' || typeof entry?.sha256 !== 'string')
|
|
53
|
+
throw new Error(`${file} entry ${prefix} lacks file and sha256`)
|
|
54
|
+
const files: unknown = entry.files
|
|
55
|
+
if (
|
|
56
|
+
files !== undefined &&
|
|
57
|
+
!(Array.isArray(files) && files.every((f) => typeof f === 'string'))
|
|
58
|
+
)
|
|
59
|
+
throw new Error(`${file} entry ${prefix} has files that are not paths`)
|
|
60
|
+
packs[prefix] = {
|
|
61
|
+
file: entry.file,
|
|
62
|
+
sha256: entry.sha256,
|
|
63
|
+
...(files ? { files } : {}),
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
packsByRoot.set(runtimeRoot, packs)
|
|
67
|
+
return packs
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** make sure the pack that owns a request path is on disk. never rejects: a
|
|
71
|
+
* pack that cannot download logs once and the file then answers 404. */
|
|
72
|
+
export async function ensureRuntimePackFor(
|
|
73
|
+
runtimeRoot: string,
|
|
74
|
+
pathname: string,
|
|
75
|
+
): Promise<void> {
|
|
76
|
+
warmRuntimePacks(runtimeRoot)
|
|
77
|
+
const prefix = RUNTIME_PACK_PREFIXES.find((candidate) =>
|
|
78
|
+
pathname.includes(`/${candidate}`),
|
|
79
|
+
)
|
|
80
|
+
if (prefix) {
|
|
81
|
+
await installNamedPack(runtimeRoot, prefix)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
const listed = listedFiles(runtimeRoot)
|
|
85
|
+
.get(path.posix.basename(pathname))
|
|
86
|
+
?.find(({ file }) => pathname.endsWith(`/${file}`))
|
|
87
|
+
if (listed) {
|
|
88
|
+
if (!fs.existsSync(path.join(runtimeRoot, listed.file)))
|
|
89
|
+
await installNamedPack(runtimeRoot, listed.pack)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
const at = pathname.indexOf(`/${RUNTIME_MERGE_PACK_DIR}`)
|
|
93
|
+
if (at === -1) return
|
|
94
|
+
const tenantDir = path.join(runtimeRoot, RUNTIME_MERGE_PACK_DIR)
|
|
95
|
+
const rel = pathname.slice(at + RUNTIME_MERGE_PACK_DIR.length + 1)
|
|
96
|
+
// the production pack holds only chunks; a missing source map or json is a plain 404
|
|
97
|
+
if (!/\.m?js$/.test(rel) || fs.existsSync(path.join(tenantDir, rel))) return
|
|
98
|
+
await installNamedPack(runtimeRoot, RUNTIME_MERGE_PACK_DIR)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function installNamedPack(runtimeRoot: string, name: string): Promise<boolean> {
|
|
102
|
+
if (name === RUNTIME_MERGE_PACK_DIR) {
|
|
103
|
+
const tenantDir = path.join(runtimeRoot, RUNTIME_MERGE_PACK_DIR)
|
|
104
|
+
return mergePack(
|
|
105
|
+
runtimeRoot,
|
|
106
|
+
name,
|
|
107
|
+
path.join(tenantDir, RUNTIME_MERGE_PACK_INSTALL),
|
|
108
|
+
tenantDir,
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
if (readPacks(runtimeRoot)?.[name]?.files)
|
|
112
|
+
return mergePack(
|
|
113
|
+
runtimeRoot,
|
|
114
|
+
name,
|
|
115
|
+
path.join(runtimeRoot, `.pack-${name}`),
|
|
116
|
+
runtimeRoot,
|
|
117
|
+
)
|
|
118
|
+
return installPack(runtimeRoot, name, path.join(runtimeRoot, name.slice(0, -1)))
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// once the first page has loaded, fetch every remaining pack one at a time so
|
|
122
|
+
// later use never waits on the network. a request for a pack joins its download;
|
|
123
|
+
// a failed download ends the warm, so an offline machine logs once.
|
|
124
|
+
const WARM_DELAY_MS = 30_000
|
|
125
|
+
const warming = new Set<string>()
|
|
126
|
+
|
|
127
|
+
function warmRuntimePacks(runtimeRoot: string): void {
|
|
128
|
+
if (warming.has(runtimeRoot)) return
|
|
129
|
+
warming.add(runtimeRoot)
|
|
130
|
+
const warm = async () => {
|
|
131
|
+
for (const name of Object.keys(readPacks(runtimeRoot) ?? {}))
|
|
132
|
+
if (!(await installNamedPack(runtimeRoot, name))) return
|
|
133
|
+
}
|
|
134
|
+
const schedule: (fn: () => void, ms: number) => NodeJS.Timeout = setTimeout
|
|
135
|
+
schedule(() => void warm(), WARM_DELAY_MS).unref()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// a merge pack installs beside the tree it belongs to, then hard-links its files
|
|
139
|
+
// into place so the tree keeps one copy on disk. a link that already exists came
|
|
140
|
+
// from an earlier or concurrent merge of the same bytes.
|
|
141
|
+
async function mergePack(
|
|
142
|
+
runtimeRoot: string,
|
|
143
|
+
name: string,
|
|
144
|
+
installDir: string,
|
|
145
|
+
targetDir: string,
|
|
146
|
+
): Promise<boolean> {
|
|
147
|
+
if (fs.existsSync(path.join(installDir, RUNTIME_MERGE_PACK_MERGED))) return true
|
|
148
|
+
if (!(await installPack(runtimeRoot, name, installDir))) return false
|
|
149
|
+
const link = (sub: string): void => {
|
|
150
|
+
for (const entry of fs.readdirSync(path.join(installDir, sub), {
|
|
151
|
+
withFileTypes: true,
|
|
152
|
+
})) {
|
|
153
|
+
const child = path.join(sub, entry.name)
|
|
154
|
+
if (entry.isDirectory()) {
|
|
155
|
+
fs.mkdirSync(path.join(targetDir, child), { recursive: true })
|
|
156
|
+
link(child)
|
|
157
|
+
} else if (entry.name !== RUNTIME_PACK_MARKER) {
|
|
158
|
+
try {
|
|
159
|
+
fs.linkSync(path.join(installDir, child), path.join(targetDir, child))
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (!(error instanceof Error && 'code' in error && error.code === 'EEXIST'))
|
|
162
|
+
throw error
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
try {
|
|
168
|
+
link('')
|
|
169
|
+
fs.writeFileSync(path.join(installDir, RUNTIME_MERGE_PACK_MERGED), '')
|
|
170
|
+
return true
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(
|
|
173
|
+
`[peach] ${name} pack merge failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
174
|
+
)
|
|
175
|
+
return false
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function installPack(
|
|
180
|
+
runtimeRoot: string,
|
|
181
|
+
prefix: string,
|
|
182
|
+
destDir: string,
|
|
183
|
+
): Promise<boolean> {
|
|
184
|
+
const marker = path.join(destDir, RUNTIME_PACK_MARKER)
|
|
185
|
+
if (fs.existsSync(marker)) return true
|
|
186
|
+
const pack = readPacks(runtimeRoot)?.[prefix]
|
|
187
|
+
// a runtime that shipped the prefix inline has no pack entry
|
|
188
|
+
if (!pack) return true
|
|
189
|
+
let install = inflight.get(destDir)
|
|
190
|
+
if (!install) {
|
|
191
|
+
const version = path.basename(runtimeRoot)
|
|
192
|
+
// the hash in the url keeps a refreshed pre-release from hitting a stale cached pack
|
|
193
|
+
const url = new URL(pack.file, rnxRuntime.tarballUrl(version))
|
|
194
|
+
url.searchParams.set('sha256', pack.sha256)
|
|
195
|
+
install = installVerifiedTarball({
|
|
196
|
+
url: url.toString(),
|
|
197
|
+
digest: { algorithm: 'sha256', encoding: 'hex', value: pack.sha256 },
|
|
198
|
+
destDir,
|
|
199
|
+
installedMarker: RUNTIME_PACK_MARKER,
|
|
200
|
+
label: `peach runtime ${version} pack ${prefix}`,
|
|
201
|
+
})
|
|
202
|
+
.catch((error: unknown) => {
|
|
203
|
+
console.error(
|
|
204
|
+
`[peach] ${prefix} pack download failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
205
|
+
)
|
|
206
|
+
})
|
|
207
|
+
.finally(() => inflight.delete(destDir))
|
|
208
|
+
inflight.set(destDir, install)
|
|
209
|
+
}
|
|
210
|
+
await install
|
|
211
|
+
return fs.existsSync(marker)
|
|
212
|
+
}
|
|
@@ -40,6 +40,9 @@ const TARGET_TYPES = [
|
|
|
40
40
|
'macro',
|
|
41
41
|
'plugin',
|
|
42
42
|
] as const
|
|
43
|
+
const targetRelative = (file: string | undefined) =>
|
|
44
|
+
file === undefined ? [] : [path.posix.normalize(file).replace(/(?<=.)\/+$/, '')]
|
|
45
|
+
|
|
43
46
|
export type SwiftManifestTargetType = (typeof TARGET_TYPES)[number]
|
|
44
47
|
const isTargetType = (value: string | undefined): value is SwiftManifestTargetType =>
|
|
45
48
|
TARGET_TYPES.some((type) => type === value)
|
|
@@ -660,11 +663,13 @@ export function swiftPackageManifest(
|
|
|
660
663
|
name,
|
|
661
664
|
type: kind,
|
|
662
665
|
dir: targetDir,
|
|
666
|
+
// swiftpm resolves these as normalized relative paths, so a directory
|
|
667
|
+
// written with a trailing slash selects its contents
|
|
663
668
|
sources:
|
|
664
669
|
target.sources == null
|
|
665
670
|
? null
|
|
666
|
-
: array(target.sources).flatMap((file) => string(file)
|
|
667
|
-
exclude: array(target.exclude).flatMap((file) => string(file)
|
|
671
|
+
: array(target.sources).flatMap((file) => targetRelative(string(file))),
|
|
672
|
+
exclude: array(target.exclude).flatMap((file) => targetRelative(string(file))),
|
|
668
673
|
dependencies,
|
|
669
674
|
languageMode,
|
|
670
675
|
mainActorIsolation,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import archives from './swift-shipped-archives.json'
|
|
1
|
+
import archives from './swift-shipped-archives.json' with { type: 'json' }
|
|
2
2
|
|
|
3
3
|
// the archive checksum of each vendor binary whose surface peach ships as a module
|
|
4
4
|
// (scripts/debug/swift-binary-standin-generate.ts writes the table). a package pinning a
|
|
@@ -48,8 +48,10 @@ export function isSwiftStandinBehavior(value: unknown): value is StandinBehavior
|
|
|
48
48
|
const fields = ['members', 'body', 'asyncBody', 'resultBody', 'set', 'stored']
|
|
49
49
|
return (
|
|
50
50
|
isRecord(value) &&
|
|
51
|
-
Object.entries(value).every(
|
|
52
|
-
|
|
51
|
+
Object.entries(value).every(([key, entry]) =>
|
|
52
|
+
key === 'builtin'
|
|
53
|
+
? entry === true
|
|
54
|
+
: fields.includes(key) && typeof entry === 'string',
|
|
53
55
|
)
|
|
54
56
|
)
|
|
55
57
|
}
|
|
@@ -157,7 +157,7 @@ export type StandinOptions = {
|
|
|
157
157
|
inert?: boolean
|
|
158
158
|
/** modules the source imports beside Foundation and UIKit. */
|
|
159
159
|
imports?: string[]
|
|
160
|
-
/** `@_exported import` lines a placeholder's
|
|
160
|
+
/** `@_exported import` lines: a placeholder's own, and the modules a framework's headers import. */
|
|
161
161
|
exported?: string[]
|
|
162
162
|
/** for a placeholder package's target: the compiler gap that forces it. */
|
|
163
163
|
placeholder?: string
|
package/src/swift-standins.ts
CHANGED
|
@@ -1501,6 +1501,8 @@ function objcStandin(
|
|
|
1501
1501
|
new Map(),
|
|
1502
1502
|
new Map(),
|
|
1503
1503
|
dependencyGraphs,
|
|
1504
|
+
'',
|
|
1505
|
+
view.imports.filter((name) => provided.has(name) || siblings.has(name)),
|
|
1504
1506
|
)
|
|
1505
1507
|
}
|
|
1506
1508
|
|
|
@@ -1533,6 +1535,9 @@ function graphStandin(
|
|
|
1533
1535
|
dependencyGraphs: SymbolGraph[] = [],
|
|
1534
1536
|
// declarations the symbol graphs cannot carry, appended after the emitted ones
|
|
1535
1537
|
trailer = '',
|
|
1538
|
+
// modules a consumer's import of this one brings along, as a framework
|
|
1539
|
+
// module map's `export *` re-exports what its headers import
|
|
1540
|
+
reexported: readonly string[] = [],
|
|
1536
1541
|
): SwiftStandin {
|
|
1537
1542
|
const paths = new Set(
|
|
1538
1543
|
graphs.flatMap((graph) =>
|
|
@@ -1626,9 +1631,11 @@ function graphStandin(
|
|
|
1626
1631
|
inheritedConformances,
|
|
1627
1632
|
projections,
|
|
1628
1633
|
dependencyGraphs,
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1634
|
+
exported: [
|
|
1635
|
+
...(placeholder?.exported ?? []),
|
|
1636
|
+
...reexported.map((name) => `@_exported import ${name}`),
|
|
1637
|
+
],
|
|
1638
|
+
...(placeholder ? { placeholder: binary.placeholder!.gap } : {}),
|
|
1632
1639
|
})
|
|
1633
1640
|
for (const entry of skipped) {
|
|
1634
1641
|
if (swiftPaths.has(entry.path))
|
package/src/vite-plugin-one.ts
CHANGED
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
serveRuntimeFile,
|
|
45
45
|
SOOTSIM_RUNTIME_MISSING_MESSAGE,
|
|
46
46
|
} from './runtime-assets'
|
|
47
|
+
import { ensureRuntimePackFor } from './runtime-packs'
|
|
47
48
|
import { SWIFT_HMR_PATH, SWIFT_PACKAGE_PATH } from './swift-submit'
|
|
48
49
|
import { swiftViteLifetime } from './swift-vite-lifetime'
|
|
49
50
|
import type { OneWidgetPackage } from './oneWidgetContract'
|
|
@@ -595,7 +596,7 @@ export function peachPlugin(options: PeachPluginOptions = {}): Plugin {
|
|
|
595
596
|
})
|
|
596
597
|
})
|
|
597
598
|
|
|
598
|
-
server.middlewares.use((req, res, next) => {
|
|
599
|
+
server.middlewares.use(async (req, res, next) => {
|
|
599
600
|
if (serveBrowserProxyRoute(req, res)) return
|
|
600
601
|
const url = req.url || ''
|
|
601
602
|
const pathname = url.split('?')[0]
|
|
@@ -615,6 +616,8 @@ export function peachPlugin(options: PeachPluginOptions = {}): Plugin {
|
|
|
615
616
|
return
|
|
616
617
|
}
|
|
617
618
|
|
|
619
|
+
await ensureRuntimePackFor(runtimeRoot, pathname)
|
|
620
|
+
|
|
618
621
|
// root-relative runtime assets emitted by the shell and engine vite
|
|
619
622
|
// builds. only intercept when the asset exists in the active runtime.
|
|
620
623
|
if (isRootRuntimeAssetPath(pathname)) {
|