sootsim 0.1.294 → 0.1.296
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 +13 -4
- package/dist-cli/bin.js +5 -5
- package/dist-cli/chunks/{agent-GDFF6G7N.js → agent-3F4K6MQK.js} +2 -2
- package/dist-cli/chunks/{agent-wrapper-ZUKQUOD2.js → agent-wrapper-5LLVCQRZ.js} +2 -2
- package/dist-cli/chunks/app-fonts-LPTY7A5G.js +4 -0
- package/dist-cli/chunks/{assert-GXOGZUKO.js → assert-R2ZUXEMS.js} +2 -2
- package/dist-cli/chunks/{auth-P7FAKLHQ.js → auth-CA62Q6IS.js} +2 -2
- package/dist-cli/chunks/{beta-RXT5RU3A.js → beta-4AXSOQLU.js} +2 -2
- package/dist-cli/chunks/{camera-RNVAC4IO.js → camera-Y4RBIONE.js} +2 -2
- package/dist-cli/chunks/{chunk-OYYQQBKP.js → chunk-222Y7NQA.js} +1 -1
- package/dist-cli/chunks/chunk-2LQOLMRK.js +4 -0
- package/dist-cli/chunks/{chunk-AFLGWOX3.js → chunk-2XUKWIAL.js} +1 -1
- package/dist-cli/chunks/{chunk-DI55E6CS.js → chunk-3PAZJSIG.js} +2 -2
- package/dist-cli/chunks/{chunk-2M7OZ6Y4.js → chunk-4MDLYOEI.js} +2 -2
- package/dist-cli/chunks/{chunk-EXTV6NWM.js → chunk-4NFGOGB7.js} +2 -2
- package/dist-cli/chunks/chunk-54TQ3ZGI.js +30 -0
- package/dist-cli/chunks/chunk-5FNPAMP4.js +125 -0
- package/dist-cli/chunks/{chunk-4Z4URZOG.js → chunk-6C24WCDK.js} +2 -2
- package/dist-cli/chunks/{chunk-KWIURH7N.js → chunk-74FOMZP7.js} +2 -2
- package/dist-cli/chunks/{chunk-JUOQ4ZPA.js → chunk-7GQWF7HE.js} +1 -1
- package/dist-cli/chunks/{chunk-BXO2PTNM.js → chunk-7V3JNANH.js} +4 -4
- package/dist-cli/chunks/{chunk-DUHBRW7J.js → chunk-7YQXQSG7.js} +1 -1
- package/dist-cli/chunks/{chunk-FSRRJGYC.js → chunk-AUBP44CY.js} +2 -2
- package/dist-cli/chunks/chunk-B2FB6IYX.js +4 -0
- package/dist-cli/chunks/{chunk-FNCSUHUR.js → chunk-B2LDO55K.js} +1 -1
- package/dist-cli/chunks/{chunk-K53YCAQM.js → chunk-BVWIR4CD.js} +1 -1
- package/dist-cli/chunks/{chunk-RWUNC66S.js → chunk-C462URRQ.js} +2 -2
- package/dist-cli/chunks/chunk-CAAADPRH.js +10 -0
- package/dist-cli/chunks/{chunk-VXGCZUX4.js → chunk-CDCVWGNU.js} +3 -3
- package/dist-cli/chunks/{chunk-WYRLRNIP.js → chunk-CS23DQB2.js} +2 -2
- package/dist-cli/chunks/chunk-DUYKBCM7.js +4 -0
- package/dist-cli/chunks/chunk-EISE56VD.js +6 -0
- package/dist-cli/chunks/{chunk-VVVORIQI.js → chunk-EJ6BAJKX.js} +1 -1
- package/dist-cli/chunks/chunk-F2MS365Z.js +4 -0
- package/dist-cli/chunks/{chunk-AMKQUZA2.js → chunk-FROCFR77.js} +1 -1
- package/dist-cli/chunks/{chunk-I233T2T4.js → chunk-FUPQIX4U.js} +2 -2
- package/dist-cli/chunks/{chunk-V2MYTL4C.js → chunk-G2SO5FPN.js} +2 -2
- package/dist-cli/chunks/chunk-GG5PWLGC.js +4 -0
- package/dist-cli/chunks/chunk-GG6XOQ5P.js +15 -0
- package/dist-cli/chunks/chunk-HSGS2U7P.js +6 -0
- package/dist-cli/chunks/{chunk-PZTKQG6H.js → chunk-IR3DUBRF.js} +5 -5
- package/dist-cli/chunks/chunk-LHUY46GE.js +4 -0
- package/dist-cli/chunks/{chunk-N4MT5KEB.js → chunk-MDBR4XTV.js} +3 -3
- package/dist-cli/chunks/{chunk-SLMUW2WU.js → chunk-MQ6QX46T.js} +2 -2
- package/dist-cli/chunks/chunk-N3FZRJT7.js +4 -0
- package/dist-cli/chunks/{chunk-ISJ4ACWL.js → chunk-NEARYPSC.js} +11 -11
- package/dist-cli/chunks/chunk-P7HBJBHH.js +4 -0
- package/dist-cli/chunks/{chunk-24BJFJFB.js → chunk-PEUWZRXA.js} +5 -5
- package/dist-cli/chunks/{chunk-PSOUSJSZ.js → chunk-QIDFGREY.js} +1 -1
- package/dist-cli/chunks/{chunk-ZUX5PIGI.js → chunk-R65XMN4D.js} +1 -1
- package/dist-cli/chunks/{chunk-AO65WEF7.js → chunk-S5Z4HKWX.js} +1 -1
- package/dist-cli/chunks/chunk-SUJSGGSS.js +23 -0
- package/dist-cli/chunks/{chunk-O6I5ZGMY.js → chunk-TIOPG6QZ.js} +1 -1
- package/dist-cli/chunks/chunk-WJVBD2UL.js +9 -0
- package/dist-cli/chunks/{chunk-D2R7PDN5.js → chunk-X7B5KJ6N.js} +1 -1
- package/dist-cli/chunks/{chunk-7HVMAGDK.js → chunk-XMVVZCIM.js} +2 -2
- package/dist-cli/chunks/chunk-ZIBKGZJD.js +4 -0
- package/dist-cli/chunks/chunk-ZPBIHFQW.js +12 -0
- package/dist-cli/chunks/cleanup-4RSKYPQ7.js +25 -0
- package/dist-cli/chunks/{cli-version-2JGMYL4O.js → cli-version-VZFF7GGM.js} +2 -2
- package/dist-cli/chunks/compat-FABLN4UI.js +20 -0
- package/dist-cli/chunks/config-74J37LKC.js +13 -0
- package/dist-cli/chunks/control-VBK32Y7F.js +4 -0
- package/dist-cli/chunks/daemon-VFO7LRR4.js +4 -0
- package/dist-cli/chunks/{debug-XUGWZLIW.js → debug-AOYA2CLR.js} +3 -3
- package/dist-cli/chunks/{desktop-IO5HPX5Z.js → desktop-G7XWDCTM.js} +3 -3
- package/dist-cli/chunks/{detox-E42OEJJZ.js → detox-QXJ3L2ZI.js} +2 -2
- package/dist-cli/chunks/{device-XA67L2EB.js → device-NZZ5ZT3Q.js} +2 -2
- package/dist-cli/chunks/{diagnose-36FD6ECF.js → diagnose-5CAEJ42M.js} +2 -2
- package/dist-cli/chunks/disk-cleanup-GZSUZJ6Y.js +4 -0
- package/dist-cli/chunks/drivers-OLJCUSPO.js +4 -0
- package/dist-cli/chunks/{film-XARTGFIZ.js → film-C25VRUNC.js} +3 -3
- package/dist-cli/chunks/flow-IW3WUF5D.js +4 -0
- package/dist-cli/chunks/help-GEVJF2DA.js +4 -0
- package/dist-cli/chunks/{hidden-runtime-alias-T4ZALBE2.js → hidden-runtime-alias-NSLIK6XT.js} +2 -2
- package/dist-cli/chunks/home-paths-OC6JZXIM.js +4 -0
- package/dist-cli/chunks/inspect-KXJNI7CU.js +4 -0
- package/dist-cli/chunks/install-desktop-IYWPRFPB.js +4 -0
- package/dist-cli/chunks/{login-GF6WVVER.js → login-LB4TMM4T.js} +4 -4
- package/dist-cli/chunks/{logout-6F6UKUNI.js → logout-33IOBD4X.js} +2 -2
- package/dist-cli/chunks/{maestro-FMPKHEMX.js → maestro-EEVXX536.js} +3 -3
- package/dist-cli/chunks/{maestro-generate-QGPGNXI6.js → maestro-generate-ICPWVXGA.js} +3 -3
- package/dist-cli/chunks/{mode-JSNMAHDZ.js → mode-6IXOZDK3.js} +2 -2
- package/dist-cli/chunks/{optional-demo-registry-K4VERLJV.js → optional-demo-registry-KBMC7SNI.js} +2 -2
- package/dist-cli/chunks/{perf-PO7ARUTG.js → perf-CTTJSAIJ.js} +2 -2
- package/dist-cli/chunks/{permissions-XI2QUAEZ.js → permissions-BFQS4DIY.js} +2 -2
- package/dist-cli/chunks/{record-4CJCJAFD.js → record-MJR5ELHE.js} +3 -3
- package/dist-cli/chunks/report-issue-FMJRNDKH.js +29 -0
- package/dist-cli/chunks/runtime-PV4HH2OB.js +4 -0
- package/dist-cli/chunks/{screenshot-command-NDVULWMZ.js → screenshot-command-MGNASAXR.js} +3 -3
- package/dist-cli/chunks/{screenshots-capture-Q7UTXF3V.js → screenshots-capture-N6PANAVT.js} +2 -2
- package/dist-cli/chunks/serve-RS6TIO7U.js +44 -0
- package/dist-cli/chunks/setup-MFYGYR2F.js +32 -0
- package/dist-cli/chunks/{skills-NFG6VAO5.js → skills-TD35LDKK.js} +2 -2
- package/dist-cli/chunks/{storage-POWCOUCQ.js → storage-VCFXDJNO.js} +2 -2
- package/dist-cli/chunks/store-VNSTKYBQ.js +4 -0
- package/dist-cli/chunks/telemetry-GPNHSYFF.js +4 -0
- package/dist-cli/chunks/{timeline-MNHZCVAL.js → timeline-UJCZAWJV.js} +2 -2
- package/dist-cli/chunks/upgrade-S24NH4YG.js +7 -0
- package/dist-cli/chunks/upload-CCKAO2M5.js +4 -0
- package/dist-cli/chunks/version-IVPWT2ZQ.js +6 -0
- package/dist-cli/chunks/web-6BMWAWJL.js +4 -0
- package/dist-cli/chunks/{what-happened-H74HHGRK.js → what-happened-HRHB6ETL.js} +2 -2
- package/dist-lib/agent-daemon-client.cjs +2 -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 +4 -1
- package/dist-lib/cli-constants.cjs +1 -1
- package/dist-lib/config.cjs +3 -2
- package/dist-lib/detox/index.cjs +1 -1
- package/dist-lib/dev-bundle-resolution.cjs +1 -1
- package/dist-lib/home-paths.cjs +41 -14
- package/dist-lib/host/bridge-host.cjs +216 -84
- 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/websocket-proxy.cjs +1 -1
- package/dist-lib/index.cjs +5 -2
- package/dist-lib/jump-to-source-babel.cjs +1 -1
- package/dist-lib/menu.cjs +2 -3
- package/dist-lib/menu.mjs +2 -3
- package/dist-lib/metro.cjs +1 -1
- package/dist-lib/profiles.cjs +1 -1
- package/dist-lib/render-mode.cjs +1 -1
- package/dist-lib/scripts/dev-server-scanner.cjs +2 -2
- package/dist-lib/sdk.cjs +1 -1
- package/dist-lib/sdk.mjs +1 -1
- package/dist-lib/skills.cjs +373 -159
- package/dist-lib/vite.cjs +3 -2
- package/package.json +2 -1
- package/scripts/postinstall.cjs +2 -78
- package/skills/contrast/SKILL.md +3 -2
- package/skills/sootsim-setup/SKILL.md +25 -0
- package/src/bridge-constants.ts +1 -0
- package/src/config.ts +4 -0
- package/src/disk-cleanup.ts +5 -26
- package/src/home-paths.ts +58 -23
- package/src/host/bridge-host.ts +152 -57
- package/src/index.ts +4 -1
- package/src/menu.ts +0 -1
- package/src/runtime-assets.ts +35 -0
- package/src/runtime-delivery.ts +5 -8
- package/src/skills/builtin/compat-check.ts +32 -65
- package/dist-cli/chunks/app-fonts-IMYBO6AQ.js +0 -4
- package/dist-cli/chunks/chunk-37ZXWXM3.js +0 -4
- package/dist-cli/chunks/chunk-3F2GAJ4Z.js +0 -6
- package/dist-cli/chunks/chunk-5S3GPTX7.js +0 -6
- package/dist-cli/chunks/chunk-6YN3IAWJ.js +0 -4
- package/dist-cli/chunks/chunk-7Q77LTPT.js +0 -15
- package/dist-cli/chunks/chunk-BBVBMGTT.js +0 -31
- package/dist-cli/chunks/chunk-CTMPC5DN.js +0 -6
- package/dist-cli/chunks/chunk-JFWEDISV.js +0 -120
- package/dist-cli/chunks/chunk-JGGXQHXH.js +0 -10
- package/dist-cli/chunks/chunk-OKSGM6IG.js +0 -4
- package/dist-cli/chunks/chunk-SPQKJDIK.js +0 -4
- package/dist-cli/chunks/chunk-W44PDEQH.js +0 -4
- package/dist-cli/chunks/chunk-WQL3BLFD.js +0 -9
- package/dist-cli/chunks/chunk-XYFP466E.js +0 -12
- package/dist-cli/chunks/chunk-ZFMJFX27.js +0 -4
- package/dist-cli/chunks/cleanup-74SKOD2P.js +0 -25
- package/dist-cli/chunks/compat-ME6HXEEU.js +0 -35
- package/dist-cli/chunks/config-PM67EWT3.js +0 -13
- package/dist-cli/chunks/control-HV7GZXAH.js +0 -4
- package/dist-cli/chunks/daemon-LKRHF5OK.js +0 -4
- package/dist-cli/chunks/disk-cleanup-DDCFGFWK.js +0 -4
- package/dist-cli/chunks/drivers-C2SH3DWS.js +0 -4
- package/dist-cli/chunks/flow-E64G5LF3.js +0 -4
- package/dist-cli/chunks/help-4ZSRG4F4.js +0 -4
- package/dist-cli/chunks/home-paths-5L7N2FLS.js +0 -4
- package/dist-cli/chunks/inspect-U2SV25ER.js +0 -4
- package/dist-cli/chunks/install-desktop-LTGVT7ID.js +0 -4
- package/dist-cli/chunks/runtime-SN2RWDES.js +0 -4
- package/dist-cli/chunks/serve-UEUIBHCK.js +0 -44
- package/dist-cli/chunks/setup-ZEEWYK4B.js +0 -99
- package/dist-cli/chunks/store-BJK7V7FL.js +0 -4
- package/dist-cli/chunks/telemetry-EJP5EW7K.js +0 -4
- package/dist-cli/chunks/upgrade-X57SIG7L.js +0 -6
- package/dist-cli/chunks/upload-JABM3ITZ.js +0 -4
- package/dist-cli/chunks/version-V4IKIDX7.js +0 -6
- package/dist-cli/chunks/web-F5CLF34I.js +0 -4
package/src/host/bridge-host.ts
CHANGED
|
@@ -27,11 +27,16 @@ import {
|
|
|
27
27
|
writeDevBridgeLockfile,
|
|
28
28
|
writeRuntimeUpgradeNotice,
|
|
29
29
|
writeSharedConfig,
|
|
30
|
+
sootsimHomeDir,
|
|
30
31
|
type DaemonLockfile,
|
|
31
32
|
type DevBridgeLockfile,
|
|
32
33
|
type SharedConfig,
|
|
33
34
|
} from '../home-paths.ts'
|
|
34
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
resolveDevCheckoutRuntimeRoot,
|
|
37
|
+
resolveInstalledRuntimeRoot,
|
|
38
|
+
runtimeVersionFromHostname,
|
|
39
|
+
} from '../runtime-assets.ts'
|
|
35
40
|
import { sootsimRuntime } from '../runtime-delivery.ts'
|
|
36
41
|
import { AgentHost, type AgentHostOptions } from './agent-host.ts'
|
|
37
42
|
import {
|
|
@@ -616,6 +621,7 @@ export class SootSimBridgeHost {
|
|
|
616
621
|
recentActions: [],
|
|
617
622
|
}
|
|
618
623
|
this.sims.set(sim.id, sim)
|
|
624
|
+
this.writeConnectedRuntimeSnapshot()
|
|
619
625
|
if (this.shouldPromoteSim(sim)) {
|
|
620
626
|
this.primarySimId = sim.id
|
|
621
627
|
}
|
|
@@ -722,6 +728,7 @@ export class SootSimBridgeHost {
|
|
|
722
728
|
sim = null
|
|
723
729
|
role = 'cli'
|
|
724
730
|
this.cliIdentityKeyBySocket.set(ws, `ws-${this.nextCliFallbackId++}`)
|
|
731
|
+
this.writeConnectedRuntimeSnapshot()
|
|
725
732
|
this.broadcastSimAssignments()
|
|
726
733
|
this.broadcastSimClientStates()
|
|
727
734
|
}
|
|
@@ -736,6 +743,7 @@ export class SootSimBridgeHost {
|
|
|
736
743
|
sim.url = registration.url
|
|
737
744
|
sim.title = registration.title
|
|
738
745
|
sim.userAgent = registration.userAgent
|
|
746
|
+
this.writeConnectedRuntimeSnapshot()
|
|
739
747
|
// kind/meta are optional and free-form. only update if the
|
|
740
748
|
// sender supplied a value so we don't accidentally clear
|
|
741
749
|
// state on a heartbeat-style re-register.
|
|
@@ -779,30 +787,20 @@ export class SootSimBridgeHost {
|
|
|
779
787
|
// tab that has the engine config global picks up the change live
|
|
780
788
|
// — same shape as electron's `config:changed` IPC.
|
|
781
789
|
if (msg.type === 'bridge:write-shared-config') {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
790
|
+
if (!msg.patch || typeof msg.patch !== 'object' || Array.isArray(msg.patch)) {
|
|
791
|
+
return
|
|
792
|
+
}
|
|
793
|
+
const patch = Object.fromEntries(
|
|
794
|
+
Object.entries(msg.patch),
|
|
795
|
+
) satisfies Partial<SharedConfig>
|
|
788
796
|
try {
|
|
789
|
-
|
|
797
|
+
this.writeAndBroadcastSharedConfig(patch)
|
|
790
798
|
} catch (err) {
|
|
791
799
|
process.stderr.write(
|
|
792
800
|
`sootsim: bridge:write-shared-config failed: ${err instanceof Error ? err.message : String(err)}\n`,
|
|
793
801
|
)
|
|
794
802
|
return
|
|
795
803
|
}
|
|
796
|
-
const payload = JSON.stringify({
|
|
797
|
-
type: 'bridge:shared-config-changed',
|
|
798
|
-
config: next,
|
|
799
|
-
})
|
|
800
|
-
for (const peer of this.sims.values()) {
|
|
801
|
-
if (peer.ws.readyState !== WebSocket.OPEN) continue
|
|
802
|
-
try {
|
|
803
|
-
peer.ws.send(payload)
|
|
804
|
-
} catch {}
|
|
805
|
-
}
|
|
806
804
|
return
|
|
807
805
|
}
|
|
808
806
|
|
|
@@ -1177,6 +1175,18 @@ export class SootSimBridgeHost {
|
|
|
1177
1175
|
|
|
1178
1176
|
private bootstrapping = true
|
|
1179
1177
|
|
|
1178
|
+
private connectedRuntimeVersions(): string[] {
|
|
1179
|
+
const versions = new Set<string>()
|
|
1180
|
+
for (const sim of this.sims.values()) {
|
|
1181
|
+
try {
|
|
1182
|
+
const url = new URL(sim.url || sim.origin || 'http://localhost')
|
|
1183
|
+
const version = runtimeVersionFromHostname(url.hostname)
|
|
1184
|
+
if (version) versions.add(version)
|
|
1185
|
+
} catch {}
|
|
1186
|
+
}
|
|
1187
|
+
return [...versions].sort()
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1180
1190
|
private buildLockfileSnapshot(): DaemonLockfile {
|
|
1181
1191
|
return {
|
|
1182
1192
|
schema: 1,
|
|
@@ -1186,6 +1196,7 @@ export class SootSimBridgeHost {
|
|
|
1186
1196
|
runtimePort: this.effectivePort,
|
|
1187
1197
|
activeRuntime: this.activeRuntimeVersion,
|
|
1188
1198
|
activeRuntimeDir: this.activeRuntimeDirPath,
|
|
1199
|
+
servedRuntimes: this.connectedRuntimeVersions(),
|
|
1189
1200
|
startedAt: this.startedAt,
|
|
1190
1201
|
heartbeatAt: Date.now(),
|
|
1191
1202
|
bootstrapping: this.bootstrapping,
|
|
@@ -1205,6 +1216,7 @@ export class SootSimBridgeHost {
|
|
|
1205
1216
|
startedAt: this.startedAt,
|
|
1206
1217
|
heartbeatAt: Date.now(),
|
|
1207
1218
|
source: 'vite-dev',
|
|
1219
|
+
servedRuntimes: this.connectedRuntimeVersions(),
|
|
1208
1220
|
}
|
|
1209
1221
|
}
|
|
1210
1222
|
|
|
@@ -1216,6 +1228,19 @@ export class SootSimBridgeHost {
|
|
|
1216
1228
|
writeDevBridgeLockfile(this.buildDevLockfileSnapshot())
|
|
1217
1229
|
}
|
|
1218
1230
|
|
|
1231
|
+
private writeConnectedRuntimeSnapshot() {
|
|
1232
|
+
if (this.shouldWriteLockfile && this.httpServer) {
|
|
1233
|
+
try {
|
|
1234
|
+
this.writeLockfileSnapshot()
|
|
1235
|
+
} catch {}
|
|
1236
|
+
}
|
|
1237
|
+
if (this.shouldWriteDevLockfile && this.httpServer) {
|
|
1238
|
+
try {
|
|
1239
|
+
this.writeDevLockfileSnapshot()
|
|
1240
|
+
} catch {}
|
|
1241
|
+
}
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1219
1244
|
private refreshActiveRuntime() {
|
|
1220
1245
|
this.activeRuntimeVersion = readActiveRuntime()
|
|
1221
1246
|
// mode boundary (see runtime-assets resolveDevCheckoutRuntimeRoot): inside
|
|
@@ -1280,12 +1305,13 @@ export class SootSimBridgeHost {
|
|
|
1280
1305
|
|
|
1281
1306
|
private startRuntimeUpdater() {
|
|
1282
1307
|
if (!this.shouldWriteLockfile || this.runtimeUpdateTimer) return
|
|
1308
|
+
if (fs.existsSync(path.join(sootsimHomeDir(), 'runtime-update-disabled'))) return
|
|
1283
1309
|
// an isolated dev daemon (explicit SOOTSIM_HOME, dev checkout, nothing
|
|
1284
1310
|
// staged) serves the repo's public/sootsim build. auto-installing the
|
|
1285
1311
|
// published runtime here would stage it active and shadow that dev build
|
|
1286
1312
|
// (runtime-assets prefers a staged runtime under an explicit home), so the
|
|
1287
1313
|
// daemon would silently validate published code instead of the checkout.
|
|
1288
|
-
//
|
|
1314
|
+
// local-publish writes the marker above when it stages a source build.
|
|
1289
1315
|
if (process.env.SOOTSIM_HOME && isSootsimDevCheckout() && !readActiveRuntime()) {
|
|
1290
1316
|
return
|
|
1291
1317
|
}
|
|
@@ -1304,7 +1330,9 @@ export class SootSimBridgeHost {
|
|
|
1304
1330
|
if (reason === 'startup') {
|
|
1305
1331
|
process.stderr.write('sootsim: checking for runtime updates…\n')
|
|
1306
1332
|
}
|
|
1307
|
-
const result = await sootsimRuntime.updateToLatest(
|
|
1333
|
+
const result = await sootsimRuntime.updateToLatest({
|
|
1334
|
+
protectVersions: this.connectedRuntimeVersions(),
|
|
1335
|
+
})
|
|
1308
1336
|
if (!result.updated || !result.latestVersion) {
|
|
1309
1337
|
if (reason === 'startup') {
|
|
1310
1338
|
process.stderr.write(
|
|
@@ -1371,6 +1399,10 @@ export class SootSimBridgeHost {
|
|
|
1371
1399
|
runtimeDir: this.activeRuntimeDirPath,
|
|
1372
1400
|
})
|
|
1373
1401
|
for (const sim of this.sims.values()) {
|
|
1402
|
+
try {
|
|
1403
|
+
const simUrl = new URL(sim.url || sim.origin || 'http://localhost')
|
|
1404
|
+
if (runtimeVersionFromHostname(simUrl.hostname)) continue
|
|
1405
|
+
} catch {}
|
|
1374
1406
|
if (sim.ws.readyState === WebSocket.OPEN) {
|
|
1375
1407
|
try {
|
|
1376
1408
|
sim.ws.send(payload)
|
|
@@ -1434,17 +1466,85 @@ export class SootSimBridgeHost {
|
|
|
1434
1466
|
return
|
|
1435
1467
|
}
|
|
1436
1468
|
|
|
1437
|
-
// only accept read methods. anything else returns 405 — we never
|
|
1438
|
-
// mutate via http; the WS channel owns all writes.
|
|
1439
1469
|
const method = (req.method || 'GET').toUpperCase()
|
|
1470
|
+
let url: URL
|
|
1471
|
+
try {
|
|
1472
|
+
url = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`)
|
|
1473
|
+
} catch {
|
|
1474
|
+
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' })
|
|
1475
|
+
res.end('invalid request URL or host')
|
|
1476
|
+
return
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// /__sootsim/shared-config is the one awaited HTTP write. ordinary
|
|
1480
|
+
// settings fan out over WS, but destructive reloads need confirmation
|
|
1481
|
+
// that the disk source of truth accepted their final reset first.
|
|
1482
|
+
if (url.pathname === '/__sootsim/shared-config') {
|
|
1483
|
+
res.setHeader('Access-Control-Allow-Origin', '*')
|
|
1484
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
|
|
1485
|
+
res.setHeader('Cache-Control', 'no-store')
|
|
1486
|
+
if (method === 'OPTIONS') {
|
|
1487
|
+
res.writeHead(204, { Allow: 'GET, HEAD, POST, OPTIONS' })
|
|
1488
|
+
res.end()
|
|
1489
|
+
return
|
|
1490
|
+
}
|
|
1491
|
+
if (method === 'GET' || method === 'HEAD') {
|
|
1492
|
+
let body = '{}'
|
|
1493
|
+
try {
|
|
1494
|
+
body = JSON.stringify(readSharedConfig())
|
|
1495
|
+
} catch {}
|
|
1496
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
1497
|
+
if (method === 'HEAD') res.end()
|
|
1498
|
+
else res.end(body)
|
|
1499
|
+
return
|
|
1500
|
+
}
|
|
1501
|
+
if (method === 'POST') {
|
|
1502
|
+
void (async () => {
|
|
1503
|
+
try {
|
|
1504
|
+
const chunks: Buffer[] = []
|
|
1505
|
+
let size = 0
|
|
1506
|
+
for await (const chunk of req) {
|
|
1507
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
|
1508
|
+
size += buffer.byteLength
|
|
1509
|
+
if (size > 8 * 1024 * 1024) {
|
|
1510
|
+
res.writeHead(413, { 'Content-Type': 'text/plain; charset=utf-8' })
|
|
1511
|
+
res.end('shared config patch is too large')
|
|
1512
|
+
return
|
|
1513
|
+
}
|
|
1514
|
+
chunks.push(buffer)
|
|
1515
|
+
}
|
|
1516
|
+
const parsed: unknown = JSON.parse(Buffer.concat(chunks).toString('utf8'))
|
|
1517
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
1518
|
+
throw new Error('shared config patch must be an object')
|
|
1519
|
+
}
|
|
1520
|
+
const patch = Object.fromEntries(
|
|
1521
|
+
Object.entries(parsed),
|
|
1522
|
+
) satisfies Partial<SharedConfig>
|
|
1523
|
+
const next = this.writeAndBroadcastSharedConfig(patch)
|
|
1524
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
1525
|
+
res.end(JSON.stringify(next))
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
res.writeHead(400, { 'Content-Type': 'text/plain; charset=utf-8' })
|
|
1528
|
+
res.end(
|
|
1529
|
+
`invalid shared config patch: ${error instanceof Error ? error.message : String(error)}`,
|
|
1530
|
+
)
|
|
1531
|
+
}
|
|
1532
|
+
})()
|
|
1533
|
+
return
|
|
1534
|
+
}
|
|
1535
|
+
res.writeHead(405, { Allow: 'GET, HEAD, POST, OPTIONS' })
|
|
1536
|
+
res.end('method not allowed')
|
|
1537
|
+
return
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
// every other daemon route is read-only unless its owning proxy handled
|
|
1541
|
+
// the mutation above.
|
|
1440
1542
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
1441
1543
|
res.writeHead(405, { Allow: 'GET, HEAD' })
|
|
1442
1544
|
res.end('method not allowed')
|
|
1443
1545
|
return
|
|
1444
1546
|
}
|
|
1445
1547
|
|
|
1446
|
-
const url = new URL(req.url || '/', 'http://localhost')
|
|
1447
|
-
|
|
1448
1548
|
// /__bundle-proxy?url=<encoded> — runtime/worker fetches metro bundles +
|
|
1449
1549
|
// assets through here. needed because workers can't directly fetch a
|
|
1450
1550
|
// cross-origin URL without CORS headers, and metro doesn't set them.
|
|
@@ -1544,42 +1644,21 @@ export class SootSimBridgeHost {
|
|
|
1544
1644
|
return
|
|
1545
1645
|
}
|
|
1546
1646
|
|
|
1547
|
-
//
|
|
1548
|
-
//
|
|
1549
|
-
//
|
|
1550
|
-
//
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
if (url.pathname === '/__sootsim/shared-config') {
|
|
1557
|
-
res.setHeader('Access-Control-Allow-Origin', '*')
|
|
1558
|
-
res.setHeader('Cache-Control', 'no-store')
|
|
1559
|
-
if (method === 'GET' || method === 'HEAD') {
|
|
1560
|
-
let body = '{}'
|
|
1561
|
-
try {
|
|
1562
|
-
body = JSON.stringify(readSharedConfig())
|
|
1563
|
-
} catch {}
|
|
1564
|
-
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
1565
|
-
if (method === 'HEAD') res.end()
|
|
1566
|
-
else res.end(body)
|
|
1567
|
-
return
|
|
1568
|
-
}
|
|
1569
|
-
res.writeHead(405, { Allow: 'GET, HEAD' })
|
|
1570
|
-
res.end('method not allowed (use the bridge over WS for writes)')
|
|
1571
|
-
return
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
// runtime may have been swapped on disk (sootsim runtime use via file
|
|
1575
|
-
// write, or another cli process) — re-read per-request so we serve
|
|
1576
|
-
// the current active version. cost is one readFileSync + one statSync.
|
|
1577
|
-
this.refreshActiveRuntime()
|
|
1578
|
-
const baseDir = this.activeRuntimeDirPath
|
|
1647
|
+
// a repo-selected app runtime opens on its own versioned localhost origin. this
|
|
1648
|
+
// keeps root-relative runtime assets on the same version without changing
|
|
1649
|
+
// the machine's default runtime or interfering with another app. ordinary
|
|
1650
|
+
// localhost requests continue to follow the active stable runtime.
|
|
1651
|
+
const requestedRuntime = runtimeVersionFromHostname(url.hostname)
|
|
1652
|
+
if (!requestedRuntime) this.refreshActiveRuntime()
|
|
1653
|
+
const baseDir = requestedRuntime
|
|
1654
|
+
? resolveInstalledRuntimeRoot(requestedRuntime)
|
|
1655
|
+
: this.activeRuntimeDirPath
|
|
1579
1656
|
if (!baseDir) {
|
|
1580
1657
|
res.writeHead(503, { 'Content-Type': 'text/plain; charset=utf-8' })
|
|
1581
1658
|
res.end(
|
|
1582
|
-
|
|
1659
|
+
requestedRuntime
|
|
1660
|
+
? `sootsim: runtime ${requestedRuntime} is not installed. run \`sootsim open\` from that app again.`
|
|
1661
|
+
: 'sootsim: no active runtime installed. run `sootsim runtime install` to fetch one.',
|
|
1583
1662
|
)
|
|
1584
1663
|
return
|
|
1585
1664
|
}
|
|
@@ -2333,6 +2412,21 @@ export class SootSimBridgeHost {
|
|
|
2333
2412
|
}
|
|
2334
2413
|
}
|
|
2335
2414
|
|
|
2415
|
+
private writeAndBroadcastSharedConfig(patch: Partial<SharedConfig>): SharedConfig {
|
|
2416
|
+
const next = writeSharedConfig(patch)
|
|
2417
|
+
const payload = JSON.stringify({
|
|
2418
|
+
type: 'bridge:shared-config-changed',
|
|
2419
|
+
config: next,
|
|
2420
|
+
})
|
|
2421
|
+
for (const peer of this.sims.values()) {
|
|
2422
|
+
if (peer.ws.readyState !== WebSocket.OPEN) continue
|
|
2423
|
+
try {
|
|
2424
|
+
peer.ws.send(payload)
|
|
2425
|
+
} catch {}
|
|
2426
|
+
}
|
|
2427
|
+
return next
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2336
2430
|
private broadcastSimClientStates() {
|
|
2337
2431
|
for (const sim of this.sims.values()) {
|
|
2338
2432
|
if (sim.ws.readyState !== WebSocket.OPEN) continue
|
|
@@ -2488,6 +2582,7 @@ export class SootSimBridgeHost {
|
|
|
2488
2582
|
expiresAt: Date.now() + SootSimBridgeHost.SIM_RECONNECT_TTL_MS,
|
|
2489
2583
|
})
|
|
2490
2584
|
this.sims.delete(sim.id)
|
|
2585
|
+
this.writeConnectedRuntimeSnapshot()
|
|
2491
2586
|
}
|
|
2492
2587
|
|
|
2493
2588
|
private getRestorableSimState(simId: string): BridgeRestorableSimState | null {
|
package/src/index.ts
CHANGED
|
@@ -6,5 +6,8 @@
|
|
|
6
6
|
export { sootsimPlugin } from './vite-plugin-one'
|
|
7
7
|
export { default as metroPlugin } from './metro-plugin'
|
|
8
8
|
export { default as jumpToSourceBabelPlugin } from './jump-to-source-babel'
|
|
9
|
-
export {
|
|
9
|
+
export {
|
|
10
|
+
DEFAULT_SOOTSIM_BRIDGE_PORT,
|
|
11
|
+
DEFAULT_SOOTSIM_BRIDGE_URL,
|
|
12
|
+
} from './bridge-constants'
|
|
10
13
|
export { DEFAULT_SOOTSIM_SHELL_URL } from './cli-constants'
|
package/src/menu.ts
CHANGED
package/src/runtime-assets.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import fs from 'fs'
|
|
6
6
|
import path from 'path'
|
|
7
7
|
import { fileURLToPath } from 'url'
|
|
8
|
+
import { isValidRuntimeVersion } from '@contrast/runtime-delivery'
|
|
8
9
|
import { readActiveRuntime, runtimeDir } from './home-paths'
|
|
9
10
|
|
|
10
11
|
export const SOOTSIM_RUNTIME_MISSING_MESSAGE =
|
|
@@ -124,6 +125,40 @@ export function resolveActiveRuntimeRoot(): string | null {
|
|
|
124
125
|
return dir
|
|
125
126
|
}
|
|
126
127
|
|
|
128
|
+
export function runtimeVersionHostname(version: string): string {
|
|
129
|
+
const trimmed = version.trim()
|
|
130
|
+
if (!isValidRuntimeVersion(trimmed)) {
|
|
131
|
+
throw new Error(`invalid SootSim runtime version: ${version}`)
|
|
132
|
+
}
|
|
133
|
+
const encoded = Buffer.from(trimmed, 'utf8').toString('hex')
|
|
134
|
+
const labels = encoded.match(/.{1,60}/g)
|
|
135
|
+
if (!labels) throw new Error(`invalid SootSim runtime version: ${version}`)
|
|
136
|
+
return `${labels.join('.')}.runtime.localhost`
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function runtimeVersionFromHostname(hostname: string): string | null {
|
|
140
|
+
const suffix = '.runtime.localhost'
|
|
141
|
+
if (!hostname.toLowerCase().endsWith(suffix)) return null
|
|
142
|
+
const labels = hostname.slice(0, -suffix.length).split('.')
|
|
143
|
+
if (labels.length === 0 || labels.some((label) => !/^[0-9a-f]{1,60}$/i.test(label))) {
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
const encoded = labels.join('')
|
|
147
|
+
if (encoded.length % 2 !== 0) return null
|
|
148
|
+
try {
|
|
149
|
+
const version = Buffer.from(encoded, 'hex').toString('utf8')
|
|
150
|
+
return isValidRuntimeVersion(version) ? version : null
|
|
151
|
+
} catch {
|
|
152
|
+
return null
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function resolveInstalledRuntimeRoot(version: string): string | null {
|
|
157
|
+
const dir = runtimeDir(version)
|
|
158
|
+
if (!fs.existsSync(path.join(dir, 'index.html'))) return null
|
|
159
|
+
return dir
|
|
160
|
+
}
|
|
161
|
+
|
|
127
162
|
export function isRootRuntimeAssetPath(pathname: string): boolean {
|
|
128
163
|
return ROOT_RUNTIME_PATHS.some((p) => pathname === p || pathname.startsWith(p))
|
|
129
164
|
}
|
package/src/runtime-delivery.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// sootsim's binding of the shared runtime-delivery machinery.
|
|
2
2
|
//
|
|
3
3
|
// the machinery itself (hosted manifest, sha256-verified tarballs, channels,
|
|
4
|
-
//
|
|
5
|
-
// about sootsim. this file supplies sootsim's identity: ~/.sootsim, the
|
|
4
|
+
// channel resolution, and auto-update) lives in @contrast/runtime-delivery and
|
|
5
|
+
// knows nothing about sootsim. this file supplies sootsim's identity: ~/.sootsim, the
|
|
6
6
|
// SOOTSIM_* env overrides, and the contrast.dev/runtimes hosting layout.
|
|
7
7
|
//
|
|
8
8
|
// @contrast/runtime-delivery is an internal workspace package that is never
|
|
@@ -23,12 +23,10 @@ import {
|
|
|
23
23
|
|
|
24
24
|
export const DEFAULT_RUNTIME_CDN_ORIGIN = 'https://contrast.dev'
|
|
25
25
|
|
|
26
|
-
/**
|
|
27
|
-
*
|
|
28
|
-
* static page checked into the Contrast repo (one MDX file per runtime release
|
|
29
|
-
* under src/features/site/changelog/sootsim-entries/), so `sootsim runtime
|
|
30
|
-
* notes` points users at the page. */
|
|
26
|
+
/** the official curated changelog and the machine-readable release feed. the
|
|
27
|
+
* SootSim site is static, so its /api traffic is owned by contrast.dev. */
|
|
31
28
|
export const SOOTSIM_CHANGELOG_URL = 'https://sootsim.com/changelog'
|
|
29
|
+
export const SOOTSIM_CHANGELOG_API_URL = 'https://contrast.dev/api/changelog/sootsim'
|
|
32
30
|
|
|
33
31
|
export const SOOTSIM_RUNTIME_PRODUCT: RuntimeProduct = {
|
|
34
32
|
id: 'sootsim',
|
|
@@ -42,7 +40,6 @@ export const SOOTSIM_RUNTIME_PRODUCT: RuntimeProduct = {
|
|
|
42
40
|
env: {
|
|
43
41
|
cdnOrigin: 'SOOTSIM_CDN_ORIGIN',
|
|
44
42
|
channel: 'SOOTSIM_RUNTIME_CHANNEL',
|
|
45
|
-
pin: 'SOOTSIM_RUNTIME_VERSION',
|
|
46
43
|
},
|
|
47
44
|
ensureHome: ensureSootsimHome,
|
|
48
45
|
configFile: configFilePath,
|
|
@@ -2,15 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import type { SootSimSkill } from '../types'
|
|
4
4
|
|
|
5
|
-
function getCompatStatus(entry: {
|
|
6
|
-
stubType: 'native' | 'works' | 'build-only'
|
|
7
|
-
versions: Array<{ coverage: number }>
|
|
8
|
-
}): 'full' | 'partial' | 'auto-stub' {
|
|
9
|
-
if (entry.stubType === 'works') return 'full'
|
|
10
|
-
if (entry.stubType === 'build-only') return 'full'
|
|
11
|
-
return entry.versions.some((version) => version.coverage >= 1) ? 'full' : 'auto-stub'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
5
|
export const skill: SootSimSkill = {
|
|
15
6
|
name: 'compat-check',
|
|
16
7
|
description: 'Check package compatibility with sootsim',
|
|
@@ -28,77 +19,53 @@ export const skill: SootSimSkill = {
|
|
|
28
19
|
},
|
|
29
20
|
},
|
|
30
21
|
async execute(params, context) {
|
|
31
|
-
const fs = await import('fs')
|
|
32
|
-
const path = await import('path')
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (params.packageName) {
|
|
37
|
-
const entry = POLYFILL_REGISTRY[params.packageName]
|
|
38
|
-
if (!entry) {
|
|
39
|
-
return {
|
|
40
|
-
success: true,
|
|
41
|
-
message: `${params.packageName}: not in registry (may be auto-stubbed if native)`,
|
|
42
|
-
data: { package: params.packageName, status: 'unknown' },
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const status = getCompatStatus(entry)
|
|
46
|
-
return {
|
|
47
|
-
success: true,
|
|
48
|
-
message: `${params.packageName}: ${status}`,
|
|
49
|
-
data: { package: params.packageName, status, ...entry },
|
|
50
|
-
}
|
|
51
|
-
}
|
|
22
|
+
const fs = await import('node:fs')
|
|
23
|
+
const path = await import('node:path')
|
|
24
|
+
const { COMPAT_RESULT_CAVEAT, formatPackageStatus, formatScanCounts, scanDeps } =
|
|
25
|
+
await import('../../../../compat/src/web.ts')
|
|
52
26
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (!fs.existsSync(pkgPath)) {
|
|
27
|
+
const packagePath = path.join(context.projectDir, 'package.json')
|
|
28
|
+
if (!fs.existsSync(packagePath)) {
|
|
56
29
|
return { success: false, message: 'No package.json found' }
|
|
57
30
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const results: Record<string, string[]> = {
|
|
63
|
-
full: [],
|
|
64
|
-
partial: [],
|
|
65
|
-
'auto-stub': [],
|
|
66
|
-
unsupported: [],
|
|
67
|
-
unknown: [],
|
|
31
|
+
const parsed: unknown = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
|
|
32
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
33
|
+
return { success: false, message: 'package.json is not an object' }
|
|
68
34
|
}
|
|
69
35
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
36
|
+
const dependencies = readDependencies(parsed)
|
|
37
|
+
const scan = params.packageName
|
|
38
|
+
? scanDeps({
|
|
39
|
+
dependencies: {
|
|
40
|
+
[params.packageName]: dependencies[params.packageName] ?? '*',
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
: scanDeps(parsed)
|
|
44
|
+
const packageResult = params.packageName ? scan.packages[0] : undefined
|
|
45
|
+
const message = packageResult
|
|
46
|
+
? `${params.packageName}: ${formatPackageStatus(packageResult)}. ${COMPAT_RESULT_CAVEAT}`
|
|
47
|
+
: `${formatScanCounts(scan)}. ${COMPAT_RESULT_CAVEAT}`
|
|
79
48
|
|
|
80
49
|
const reportPath = path.join(context.outputDir, 'compat-report.json')
|
|
81
50
|
fs.mkdirSync(path.dirname(reportPath), { recursive: true })
|
|
82
|
-
fs.writeFileSync(reportPath, JSON.stringify(
|
|
51
|
+
fs.writeFileSync(reportPath, `${JSON.stringify(scan, null, 2)}\n`)
|
|
83
52
|
|
|
84
53
|
return {
|
|
85
|
-
success:
|
|
86
|
-
message
|
|
54
|
+
success: true,
|
|
55
|
+
message,
|
|
87
56
|
artifacts: [{ type: 'report', name: 'compat-report', path: reportPath }],
|
|
88
|
-
data:
|
|
57
|
+
data: scan,
|
|
89
58
|
}
|
|
90
59
|
},
|
|
91
60
|
},
|
|
92
61
|
],
|
|
93
62
|
}
|
|
94
63
|
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
name === 'expo'
|
|
103
|
-
)
|
|
64
|
+
function readDependencies(value: object): Record<string, string> {
|
|
65
|
+
const dependencies = Reflect.get(value, 'dependencies')
|
|
66
|
+
const devDependencies = Reflect.get(value, 'devDependencies')
|
|
67
|
+
return {
|
|
68
|
+
...(dependencies && typeof dependencies === 'object' ? dependencies : {}),
|
|
69
|
+
...(devDependencies && typeof devDependencies === 'object' ? devDependencies : {}),
|
|
70
|
+
}
|
|
104
71
|
}
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{d as p}from"./chunk-37ZXWXM3.js";import"./chunk-D2R7PDN5.js";import{existsSync as h,mkdirSync as d,readFileSync as y,writeFileSync as g}from"node:fs";import{join as m}from"node:path";import{gzipSync as w}from"node:zlib";function f(t,e){let o=t.indexOf(`--${e}`);if(o!==-1&&o+1<t.length)return t[o+1];let i=t.find(r=>r.startsWith(`--${e}=`));return i?i.slice(e.length+3):void 0}async function S(t){let e=f(t,"wire")||"",o=f(t,"out-dir"),i=f(t,"manifest");if((!o||!i)&&(console.error(" usage: sootsim app-fonts stage --wire <wire> --out-dir <dir> --manifest <path>"),process.exit(1)),!e.trim()){console.log("no app fonts declared \u2014 nothing to stage");return}d(o,{recursive:!0});let r=await p(e,{onStaged:({url:n,byteLength:s})=>console.log(` staged app font: ${n} (${(s/1024).toFixed(1)} KiB)`),onError:(n,s)=>console.error(` warning: failed to fetch app font ${n}: ${s instanceof Error?s.message:s}`)}),a=[];for(let n of r){let s=w(n.bytes);g(m(o,n.urlhash),s),a.push({url:n.url,urlhash:n.urlhash,contentType:n.contentType,encoding:"gzip",sizeBytes:s.length,rawBytes:n.bytes.byteLength})}let c=[];if(h(i))try{let n=JSON.parse(y(i,"utf8"));Array.isArray(n)&&(c=n)}catch{}let u=new Set(c.map(n=>n.urlhash)),l=[...c,...a.filter(n=>!u.has(n.urlhash))];g(i,JSON.stringify(l)),console.log(`staged ${a.length} app font(s); manifest now has ${l.length} file(s)`)}async function E(t){let e=t[0];if(e==="stage"){await S(t.slice(1));return}console.error(` unknown app-fonts subcommand: ${e??"(none)"}`),console.error(" available: stage"),process.exit(1)}export{E as runAppFonts};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{createHash as U}from"node:crypto";import w from"node:fs";import{createRequire as D}from"node:module";import s from"node:path";var R=/\.(ttf|otf|woff|woff2)$/i;function N(t,e,o){if(!t||typeof t!="object")return;let n=t.fonts;if(Array.isArray(n))for(let i of n)typeof i=="string"&&o.push(i);let r=t[e];if(!r||typeof r!="object")return;let c=r.fonts;if(Array.isArray(c))for(let i of c){if(typeof i=="string"){o.push(i);continue}if(!i||typeof i!="object")continue;let f=i.fontDefinitions;if(Array.isArray(f))for(let y of f){if(!y||typeof y!="object")continue;let h=y.path;typeof h=="string"&&o.push(h)}}}function C(t,e){if(!Array.isArray(t))return[];let o=[];for(let n of t){let r=Array.isArray(n)?n[0]:n;if(typeof r!="string"||!/(^|\/)expo-font$/.test(r))continue;let c=Array.isArray(n)?n[1]??{}:{};N(c,e,o)}return o}function L(t){return t.slice(t.lastIndexOf("/")+1).replace(R,"")}function S(t,e){let o=e.replace(/\/+$/,""),n=new Set,r=[];for(let c of t){let i=c.replace(/^\.\//,"").replace(/^\/+/,"");!i||n.has(i)||(n.add(i),R.test(i)&&r.push({family:L(i),url:`${o}/assets/${i}`}))}return r}var k=D(import.meta.url);function _(t){try{let e=new URL(t),o=e.hostname.replace(/^\[|\]$/g,"").toLowerCase();if(o==="localhost"||o.endsWith(".localhost")||o==="0.0.0.0"||o==="::1"||/^127(?:\.\d{1,3}){3}$/.test(o))return e.origin}catch{}return null}var M=["app.config.js","app.config.ts","app.config.cjs","app.config.mjs","app.json"];function $(t){for(let e of M)try{if(w.statSync(s.join(t,e)).isFile())return!0}catch{}return!1}var W=new Set(["node_modules","artifacts","dist","build","out","coverage","tmp","public","ios","android","macos","windows","__fixtures__","__tests__","examples","example"]);function T(t,e){$(t)&&e.push(t);let o;try{o=w.readdirSync(t,{withFileTypes:!0})}catch{return}for(let n of o)n.isDirectory()&&(n.name.startsWith(".")||W.has(n.name)||T(s.join(t,n.name),e))}function q(t){try{return w.statSync(s.join(t,"package.json")).isFile()}catch{return!1}}function Q(t){let e=_(t.bundleUrl);if(!e)return[];let o=t.repoDir||process.env.CONTRAST_REPO_DIR,n=o?s.resolve(o):process.cwd();if(!o&&!q(n))return[];let r=[];o?(T(n,r),r.includes(n)||r.push(n)):r.push(n);let c=(t.platform||process.env.CONTRAST_PLATFORM||"ios").toLowerCase(),i=null;if(o||$(n))try{let l=k.resolve("@expo/config",{paths:[...r,n]}),p=k(l);typeof p.getConfig=="function"&&(i=p.getConfig)}catch{}let f=n,y=[];if(i)for(let l of r)try{let p=i(l,{skipSDKVersionRequirement:!0});if(p?.exp&&typeof p.exp=="object"){let u=p.exp.plugins;if(Array.isArray(u)){f=l,y=u;break}}}catch{}let h=C(y,c==="android"?"android":"ios"),A=(l,p)=>{let u=[l];for(;u.length>0;){let a=u.pop();if(!a)continue;let g;try{g=w.readdirSync(a,{withFileTypes:!0})}catch{continue}for(let d of g){let m=s.join(a,d.name);if(d.isDirectory()){u.push(m);continue}if(!d.isFile()||!/\.(ttf|otf|woff|woff2)$/i.test(d.name))continue;let F=s.relative(l,m).split(s.sep).join("/");h.push(s.posix.join(p,F))}}};try{let l=JSON.parse(w.readFileSync(s.join(f,"package.json"),"utf8")),p=new Set;for(let a of["dependencies","devDependencies","optionalDependencies"]){let g=Reflect.get(l,a);if(!(!g||typeof g!="object"))for(let d of Object.keys(g))p.add(d)}let u=[...p].filter(a=>a==="react-native-vector-icons"||a.startsWith("@react-native-vector-icons/"));if(u.length>0){let a=Reflect.get(l,"reactNativeVectorIcons"),g=a&&typeof a=="object"?Reflect.get(a,"fontDir"):null,d=typeof g=="string"?g:"rnvi-fonts",m=s.resolve(f,d),F=s.relative(f,m);!F.startsWith("..")&&!s.isAbsolute(F)&&A(m,F.split(s.sep).join("/"));let E=D(s.join(f,"package.json"));for(let x of u)try{let j=E.resolve(x),O=x.split("/").join(s.sep),v=`${s.sep}node_modules${s.sep}${O}${s.sep}`,P=j.lastIndexOf(v);if(P===-1)continue;let I=j.slice(0,P+v.length-1),b=x==="react-native-vector-icons"?"Fonts":"fonts";A(s.join(I,b),s.posix.join("node_modules",x,b))}catch{}}}catch{}return S(h,e)}async function Y(t,e){let o=_(t);if(!o)return null;let n=new AbortController,r=setTimeout(()=>n.abort(),e?.timeoutMs??1500);try{let c=await fetch(`${o}/`,{headers:{"expo-platform":"ios",Accept:"application/json"},signal:n.signal});if(!c.ok)return null;let f=(await c.json())?.extra?.expoClient?._internal?.projectRoot;return typeof f=="string"&&f.length>0?f:null}catch{return null}finally{clearTimeout(r)}}function Z(t){return t.map(e=>`${encodeURIComponent(e.url)}::${encodeURIComponent(e.family)}`).join(",")}function G(t){let e=[];for(let o of t.split(",")){let n=o.trim();if(!n)continue;let r=n.indexOf("::"),c=r===-1?n:n.slice(0,r);try{e.push(decodeURIComponent(c))}catch{e.push(c)}}return e.filter(Boolean)}function J(t){let e=(t.split("?")[0].split(".").pop()||"").toLowerCase();return e==="ttf"?"font/ttf":e==="otf"?"font/otf":e==="woff"?"font/woff":e==="woff2"?"font/woff2":"application/octet-stream"}async function tt(t,e){let o=[],n=new Set;for(let r of G(t)){let c=U("sha256").update(r).digest("hex");if(!n.has(c)){n.add(c);try{let i=await fetch(r);if(!i.ok){e?.onError?.(r,new Error(`${i.status} ${i.statusText}`));continue}let f=new Uint8Array(await i.arrayBuffer());o.push({url:r,urlhash:c,contentType:J(r),bytes:f}),e?.onStaged?.({url:r,byteLength:f.byteLength})}catch(i){e?.onError?.(r,i)}}}return o}export{Q as a,Y as b,Z as c,tt as d};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{createInterface as i}from"readline";var c=()=>i({input:process.stdin,output:process.stdout});function s(o){return new Promise(n=>{let r=c();r.question(o,t=>{r.close(),n(t.trim())})})}async function u(o,n=!0){let r=n?"[Y/n]":"[y/N]";if(!process.stdin.isTTY)return console.log(` ${o} ${r} ${n?"y":"n"} (non-interactive)`),n;let t=await s(` ${o} ${r} `);return t===""?n:t.toLowerCase().startsWith("y")}async function a(o,n){console.log(`
|
|
5
|
-
${o}
|
|
6
|
-
`);for(let e=0;e<n.length;e++)console.log(` ${e+1}. ${n[e]}`);if(console.log(),!process.stdin.isTTY)return console.log(` choose [1-${n.length}]: 1 (non-interactive)`),0;let r=await s(` choose [1-${n.length}]: `),t=parseInt(r,10);return t>=1&&t<=n.length?t-1:0}export{u as a,a as b};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{d as h}from"./chunk-CTMPC5DN.js";import{b as w}from"./chunk-AO65WEF7.js";import{G as v,K as B,M as C,g as S}from"./chunk-WQL3BLFD.js";import a from"node:fs";import c from"node:path";var g=1,E=`automatic-cleanup-v${g}.json`,L=`automatic-cleanup-v${g}.lock`,P=360*60*1e3;function k(){let i=c.join(S(),E);try{return JSON.parse(a.readFileSync(i,"utf8")).generation===g}catch{return!1}}function M(i){if(i<1024)return`${i} B`;let e=["KiB","MiB","GiB","TiB"],n=i/1024,r=e[0];for(let s=1;s<e.length&&n>=1024;s++)n/=1024,r=e[s];return`${n.toFixed(n>=10?1:2)} ${r}`}function p(i){let e;try{e=a.lstatSync(i)}catch{return 0}let n=e.blocks>0?e.blocks*512:e.size;if(!e.isDirectory()||e.isSymbolicLink())return n;let r=n;try{for(let s of a.readdirSync(i))r+=p(c.join(i,s))}catch{}return r}function R(i){for(let e of["SingletonLock","SingletonSocket","lockfile"])try{if(a.lstatSync(c.join(i,e)))return!0}catch{}return!1}function b(i,e){let n;try{n=a.readdirSync(i,{withFileTypes:!0})}catch{return[]}let r=R(e),s=[];for(let t of n){if(!t.isDirectory()||t.isSymbolicLink())continue;let o=c.join(i,t.name);if(w(t.name)){s.push({path:o,bytes:p(o),kind:"browser-cache",state:r?"in-use":"safe",selected:!r,detail:r?"browser profile is open; close it before cleanup":"disposable browser HTTP, bytecode, or GPU cache"});continue}s.push(...b(o,e))}return s}function U(i){let e=c.join(i,"profiles","playwright"),n;try{n=a.readdirSync(e,{withFileTypes:!0})}catch{return[]}return n.flatMap(r=>{if(!r.isDirectory()||r.isSymbolicLink())return[];let s=c.join(e,r.name);return b(s,s)})}function V(i){let e=c.join(i,"electron","userData");return b(e,e)}function D(i,e){let n=c.join(i,"recordings"),r;try{r=a.readdirSync(n,{withFileTypes:!0})}catch{return[]}return r.map(s=>{let t=c.join(n,s.name);return{path:t,bytes:p(t),kind:"recording",state:"aggressive",selected:e,detail:"user-created recording or captured-frame output"}})}function A(){let i=B();return!C(i)||!i.activeRuntime?[]:[i.activeRuntime]}function x(i){let e=new Set(A()),n=[...e],r=h.planLocalRetention({retainVersions:2,protectVersions:n}),s=h.planLocalRetention({retainVersions:1,protectVersions:n}),t=new Set(r.removeVersions),o=new Set(s.removeVersions),u=new Set([...r.keepVersions,...r.removeVersions,...s.keepVersions,...s.removeVersions]),m=[];for(let l of[...u].sort()){let d=h.product.runtimeDir(l),y="retained",f="retained runtime";e.has(l)&&l!==r.activeVersion&&l!==r.pinnedVersion?(y="in-use",f="runtime is served by the running daemon"):t.has(l)?(y="safe",f="inactive runtime outside the two-version rollback window"):o.has(l)?(y="aggressive",f="most recent rollback runtime"):l===r.activeVersion?f="active runtime; always protected":l===r.pinnedVersion&&(f="pinned runtime; always protected"),m.push({path:d,bytes:p(d),kind:"runtime-version",state:y,selected:y==="safe"||y==="aggressive"&&i,detail:f})}for(let l of r.removeCacheFiles)m.push({path:l,bytes:p(l),kind:"runtime-archive",state:"safe",selected:!0,detail:"downloaded archive is unnecessary after successful extraction"});return{activeRuntime:r.activeVersion,pinnedRuntime:r.pinnedVersion,entries:m}}function T(i={}){let e=i.aggressive===!0,n=S(),r=x(e),s=[...r.entries,...U(n),...V(n),...D(n,e)].sort((t,o)=>o.bytes-t.bytes||t.path.localeCompare(o.path));return{home:n,activeRuntime:r.activeRuntime,pinnedRuntime:r.pinnedRuntime,entries:s,reclaimableBytes:s.reduce((t,o)=>t+(o.selected?o.bytes:0),0),protectedBytes:s.reduce((t,o)=>t+(o.selected?0:o.bytes),0),totalBytes:p(n)}}function I(i,e){let n=c.relative(i,e);if(!n||n.startsWith("..")||c.isAbsolute(n))throw new Error(`refusing cleanup target outside the SootSim home: ${e}`)}function F(i={}){let e=T(i),n=i.aggressive===!0?1:2;h.pruneLocalRetention({retainVersions:n,protectVersions:A()});for(let t of e.entries)if(t.selected&&!(t.kind==="runtime-archive"||t.kind==="runtime-version")){if(t.kind==="browser-cache"){let u=c.relative(e.home,t.path).split(c.sep),m=u[0]==="profiles"&&u[1]==="playwright"&&u[2]?c.join(e.home,"profiles","playwright",u[2]):u[0]==="electron"&&u[1]==="userData"?c.join(e.home,"electron","userData"):null;if(m&&R(m)){t.state="in-use",t.selected=!1,t.detail="browser profile opened while cleanup was starting";continue}}I(e.home,t.path),a.rmSync(t.path,{recursive:!0,force:!0})}let r=e.entries.filter(t=>t.selected&&!a.existsSync(t.path)).map(t=>t.path),s=p(e.home);return{...e,removedPaths:r,reclaimedBytes:Math.max(0,e.totalBytes-s),remainingBytes:s}}function K(){v();let i=S(),e=c.join(i,E),n=c.join(i,L);if(k())return{status:"already-complete",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};let r=null;for(let s=0;s<2&&r===null;s++)try{let t=a.openSync(n,"wx");try{a.writeFileSync(t,`${JSON.stringify({pid:process.pid,startedAt:Date.now()})}
|
|
5
|
-
`,"utf8"),r=t}catch(o){a.closeSync(t);try{a.unlinkSync(n)}catch{}throw o}}catch(t){if((t instanceof Error&&"code"in t?t.code:null)!=="EEXIST")throw t;let u=0,m=0,l=null;try{l=a.lstatSync(n);let d=JSON.parse(a.readFileSync(n,"utf8"));u=typeof d.pid=="number"?d.pid:0,m=typeof d.startedAt=="number"?d.startedAt:0}catch{}if(u>0&&m>0&&Date.now()-m<P)try{return process.kill(u,0),{status:"busy",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0}}catch{}if(!l)continue;try{let d=a.lstatSync(n);if(d.dev!==l.dev||d.ino!==l.ino)return{status:"busy",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};a.unlinkSync(n)}catch{}}if(r===null)return{status:"busy",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};try{if(k())return{status:"already-complete",reclaimedBytes:0,remainingBytes:0,deferredInUseBytes:0};let s=F(),t=s.entries.reduce((u,m)=>u+(m.state==="in-use"?m.bytes:0),0);if(t>0)return{status:"deferred",reclaimedBytes:s.reclaimedBytes,remainingBytes:s.remainingBytes,deferredInUseBytes:t};let o=`${e}.${process.pid}.tmp`;return a.writeFileSync(o,`${JSON.stringify({generation:g,completedAt:Date.now(),reclaimedBytes:s.reclaimedBytes})}
|
|
6
|
-
`,"utf8"),a.renameSync(o,e),{status:"complete",reclaimedBytes:s.reclaimedBytes,remainingBytes:s.remainingBytes,deferredInUseBytes:0}}finally{a.closeSync(r);try{a.unlinkSync(n)}catch{}}}export{k as a,M as b,T as c,F as d,K as e};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
/*! sootsim v0.1.294 | (c) 2026 Tamagui LLC | Proprietary — see LICENSE */
|
|
2
|
-
import { createRequire as __sootsimCreateRequire } from 'node:module'
|
|
3
|
-
var require = __sootsimCreateRequire(import.meta.url)
|
|
4
|
-
import{createHash as D}from"node:crypto";import y from"node:fs";import{createRequire as I}from"node:module";import h from"node:path";var w=/(^|\/)expo-splash-screen$/,R=/(^|\/)react-native-bootsplash$/;function U(n,e){let r=e.replace(/\/+$/,""),o=n.replace(/^\.\//,"").replace(/^\/+/,"");if(o)return`${r}/assets/${o}`}function d(n,e,r){return r?r(n):U(n,e)}function C(n){if(n==="cover")return"cover";if(n==="contain"||n==="native")return"contain"}function p(n){return typeof n=="string"&&n.length>0?n:void 0}function k(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}function v(n,e,r){if(!n||typeof n!="object")return null;let o=n,s={source:"expo-splash-screen",backgroundColor:p(o.backgroundColor)??"#ffffff"},a=p(o.image);a&&(s.imageUrl=d(a,e,r));let t=k(o.imageWidth);t!=null&&(s.imageWidthPx=t);let c=C(o.resizeMode);c&&(s.resizeMode=c);let l=o.dark;if(l&&typeof l=="object"){let u=l,g=p(u.backgroundColor),f=p(u.image);(g||f)&&(s.dark={},g&&(s.dark.backgroundColor=g),f&&(s.dark.imageUrl=d(f,e,r)))}return s}function j(n,e,r,o){let i=n[e],s=i&&typeof i=="object"?i.splash:void 0,a=n.splash,t=s&&typeof s=="object"?s:a;if(!t||typeof t!="object")return null;let l={source:"expo-legacy",backgroundColor:p(t.backgroundColor)??"#ffffff",imageWidthPx:k(t.imageWidth)??200,resizeMode:C(t.resizeMode)??"contain"},u=p(t.image);u&&(l.imageUrl=d(u,r,o));let g=t.dark;if(g&&typeof g=="object"){let f=g,S=p(f.backgroundColor),m=p(f.image);(S||m)&&(l.dark={},S&&(l.dark.backgroundColor=S),m&&(l.dark.imageUrl=d(m,r,o)))}return l}function _(n,e,r){if(!n||typeof n!="object")return null;let o=n,i=p(o.logo);if(!i)return null;let s={source:"react-native-bootsplash",backgroundColor:p(o.background)??"#fff",imageUrl:d(i,e,r),imageWidthPx:k(o.logoWidth)??100,resizeMode:"contain"},a=p(o.darkBackground),t=p(o.darkLogo);return(a||t)&&(s.dark={},a&&(s.dark.backgroundColor=a),t&&(s.dark.imageUrl=d(t,e,r))),s}function b(n,e,r,o={}){if(!n||typeof n!="object")return null;let i=n,s=i.plugins,a=null,t=null;if(Array.isArray(s))for(let c of s){let l=Array.isArray(c)?c[0]:c;if(typeof l!="string")continue;let u=Array.isArray(c)?c[1]??{}:{};!a&&w.test(l)?a=v(u,r,o.resolveAssetUrl):!t&&R.test(l)&&(t=_(u,r,o.resolveAssetUrl))}return t||a||j(i,e,r,o.resolveAssetUrl)}var x=I(import.meta.url);function E(n){try{let e=new URL(n),r=e.hostname.replace(/^\[|\]$/g,"").toLowerCase();if(r==="localhost"||r.endsWith(".localhost")||r==="0.0.0.0"||r==="::1"||/^127(?:\.\d{1,3}){3}$/.test(r))return e.origin}catch{}return null}var L=["app.config.js","app.config.ts","app.config.cjs","app.config.mjs","app.json"];function P(n){for(let e of L)try{if(y.statSync(h.join(n,e)).isFile())return!0}catch{}return!1}var F=new Set(["node_modules","artifacts","dist","build","out","coverage","tmp","public","ios","android","macos","windows","__fixtures__","__tests__","examples","example"]);function A(n,e){P(n)&&e.push(n);let r;try{r=y.readdirSync(n,{withFileTypes:!0})}catch{return}for(let o of r)o.isDirectory()&&(o.name.startsWith(".")||F.has(o.name)||A(h.join(n,o.name),e))}function M(n){try{return y.statSync(h.join(n,"package.json")).isFile()}catch{return!1}}function H(n){let e=E(n.bundleUrl);if(!e)return null;let r=n.repoDir||process.env.CONTRAST_REPO_DIR,o=r?h.resolve(r):process.cwd();if(!r&&!M(o))return null;let i=[];if(r)A(o,i),i.includes(o)||i.push(o);else{if(!P(o))return null;i.push(o)}if(i.length===0)return null;let s=(n.platform||process.env.CONTRAST_PLATFORM||"ios").toLowerCase(),a=null;try{let c=x.resolve("@expo/config",{paths:[...i,o]}),l=x(c);typeof l.getConfig=="function"&&(a=l.getConfig)}catch{}if(!a)return null;let t;for(let c of i)try{let l=a(c,{skipSDKVersionRequirement:!0});if(l?.exp&&typeof l.exp=="object"){t=l.exp;break}}catch{}return!t||typeof t!="object"?null:b(t,s==="android"?"android":"ios",e)}function J(n){return encodeURIComponent(JSON.stringify(n))}function z(n){if(!n)return null;try{let e=JSON.parse(decodeURIComponent(n));if(!e||typeof e!="object")return null;let r=e;return r.source!=="expo-splash-screen"&&r.source!=="expo-legacy"&&r.source!=="react-native-bootsplash"||typeof r.backgroundColor!="string"?null:e}catch{return null}}function W(n){let e=new Set;return n.imageUrl&&e.add(n.imageUrl),n.dark?.imageUrl&&e.add(n.dark.imageUrl),[...e]}function N(n){let e=(n.split("?")[0].split(".").pop()||"").toLowerCase();return e==="png"?"image/png":e==="jpg"||e==="jpeg"?"image/jpeg":e==="webp"?"image/webp":e==="gif"?"image/gif":e==="svg"?"image/svg+xml":"application/octet-stream"}async function K(n,e){let r=z(n);if(!r)return[];let o=[],i=new Set;for(let s of W(r)){let a=D("sha256").update(s).digest("hex");if(!i.has(a)){i.add(a);try{let t=await fetch(s);if(!t.ok){e?.onError?.(s,new Error(`${t.status} ${t.statusText}`));continue}let c=new Uint8Array(await t.arrayBuffer());o.push({url:s,urlhash:a,contentType:N(s),bytes:c}),e?.onStaged?.({url:s,byteLength:c.byteLength})}catch(t){e?.onError?.(s,t)}}}return o}export{H as a,J as b,K as c};
|