11#!/usr/bin/env node
2- import { main , suspend , until } from "effection" ;
2+ import { main , suspend , until , type Operation } from "effection" ;
33import { pathToFileURL } from "node:url" ;
44import type {
55 FoundationSimulator ,
66 FoundationSimulatorListening ,
77} from "@simulacrum/foundation-simulator" ;
88
9+ function guardedFactory (
10+ factory : Function
11+ ) : ( initData ?: unknown ) => Promise < FoundationSimulator < any >> {
12+ return async function startSimulation ( initData ?: unknown ) {
13+ const sim = await factory ( initData ) ;
14+ if ( "listen" in sim && typeof sim . listen === "function" ) {
15+ return sim as FoundationSimulator < any > ;
16+ }
17+ throw new Error ( "factory did not return a simulator instance" ) ;
18+ } ;
19+ }
20+
21+ function * normalizeSimulatorFactory ( url : string ) {
22+ try {
23+ const mod : unknown = yield * until ( import ( url ) ) ;
24+
25+ // dynamically import module has to be an object if correctly resolved
26+ if ( mod && typeof mod === "object" ) {
27+ const m = mod ;
28+
29+ // export default as factory
30+ if ( "default" in m && typeof m . default === "function" ) {
31+ const factory = m . default ;
32+ return guardedFactory ( factory ) ;
33+ }
34+
35+ // export named 'simulation' as factory
36+ if ( "simulation" in m && typeof m . simulation === "function" ) {
37+ const factory = m . simulation ;
38+ return guardedFactory ( factory ) ;
39+ }
40+ }
41+ } catch ( err ) {
42+ // no-op - will throw in fall through below
43+ }
44+ throw new Error ( "no factory or simulator instance found in module" ) ;
45+ }
46+
947main ( function * ( ) {
1048 const args = process . argv . slice ( 2 ) ;
1149 if ( args . length < 1 ) {
@@ -15,37 +53,37 @@ main(function* () {
1553 const modulePath = args [ 0 ] ;
1654
1755 // Resolve and import module inside the operation
18- let mod : any ;
19- try {
20- const url =
21- modulePath . startsWith ( "./" ) || modulePath . startsWith ( "/" )
22- ? pathToFileURL ( modulePath ) . href
23- : modulePath ;
24- mod = yield * until ( import ( url ) ) ;
25- } catch ( err ) {
26- throw new Error ( `failed to import module: ${ String ( err ) } ` ) ;
27- }
56+ const url =
57+ modulePath . startsWith ( "./" ) || modulePath . startsWith ( "/" )
58+ ? pathToFileURL ( modulePath ) . href
59+ : modulePath ;
60+ const factory = yield * normalizeSimulatorFactory ( url ) ;
2861
29- const exportNames = [ "default" , "simulation" ] ;
30- let factory : Function | undefined = undefined ;
31- for ( const name of exportNames ) {
32- if ( name in mod && typeof mod [ name ] === "function " ) {
33- factory = mod [ name ] ;
34- break ;
62+ let simulacrumPort : number | undefined = undefined ;
63+ // parse optional flags after modulePath
64+ for ( let i = 1 ; i < args . length ; i ++ ) {
65+ if ( args [ i ] === "--simulacrum-port " ) {
66+ simulacrumPort = Number ( args [ i + 1 ] ) ;
67+ i ++ ;
3568 }
3669 }
37- // fallback: module itself is a function
38- if ( ! factory && typeof mod === "function" ) factory = mod ;
3970
40- if ( ! factory ) {
41- throw new Error ( `no factory function found in module: ${ modulePath } ` ) ;
71+ // if present fetch the data chunk and pass it to the factory
72+ let initData : JSON | undefined = undefined ;
73+ if ( typeof simulacrumPort === "number" && ! Number . isNaN ( simulacrumPort ) ) {
74+ try {
75+ const res = yield * until (
76+ fetch ( `http://127.0.0.1:${ simulacrumPort } /data` )
77+ ) ;
78+ initData = yield * until ( res . json ( ) ) ;
79+ } catch ( err ) {
80+ // ignore fetch failures
81+ console . error ( "failed to fetch simulacrum data:" , err ) ;
82+ }
4283 }
4384
44- let sim = factory ( ) as FoundationSimulator < any > ;
45-
46- if ( ! sim || typeof sim . listen !== "function" ) {
47- throw new Error ( "factory did not return a simulator with .listen()" ) ;
48- }
85+ // invoke factory; it may return a simulator instance or a Promise thereof
86+ const sim = yield * until ( factory ( initData ) ) ;
4987
5088 let listening : FoundationSimulatorListening < any > | undefined = undefined ;
5189 try {
0 commit comments