1- import { mkdtempSync , writeFileSync } from 'node:fs' ;
1+ import { mkdtempSync , realpathSync , writeFileSync } from 'node:fs' ;
22import { tmpdir } from 'node:os' ;
33import { join } from 'node:path' ;
4- import { describe , expect , it } from 'vitest' ;
4+ import { afterEach , describe , expect , it } from 'vitest' ;
5+ import { unstable_readConfig } from 'wrangler' ;
56import { resolveWranglerConfig } from '../../src/vite/wranglerConfig' ;
67
8+ // `resolveWranglerConfig` locates the config via wrangler's own discovery from
9+ // `process.cwd()` (unless given an explicit path), so each test writes its
10+ // fixtures to a temp dir and switches the cwd into it. `realpathSync` collapses
11+ // the macOS `/var` → `/private/var` symlink so path assertions match what
12+ // wrangler resolves against the (real) cwd.
713function writeTempDir ( files : Record < string , string > ) : string {
8- const dir = mkdtempSync ( join ( tmpdir ( ) , 'sentry-cf-' ) ) ;
14+ const dir = realpathSync ( mkdtempSync ( join ( tmpdir ( ) , 'sentry-cf-' ) ) ) ;
915 for ( const [ name , content ] of Object . entries ( files ) ) {
1016 writeFileSync ( join ( dir , name ) , content ) ;
1117 }
18+ process . chdir ( dir ) ;
1219 return dir ;
1320}
1421
1522describe ( 'resolveWranglerConfig' , ( ) => {
23+ const originalCwd = process . cwd ( ) ;
24+ afterEach ( ( ) => {
25+ process . chdir ( originalCwd ) ;
26+ } ) ;
27+
1628 it ( 'parses wrangler.toml' , ( ) => {
1729 const dir = writeTempDir ( {
1830 'wrangler.toml' : [
@@ -27,8 +39,8 @@ describe('resolveWranglerConfig', () => {
2739 const result = resolveWranglerConfig ( dir ) ;
2840 expect ( result ) . toBeDefined ( ) ;
2941 // wrangler resolves `main` to an absolute path against the config dir.
30- expect ( result ! . config . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
31- expect ( result ! . config . durableObjects ) . toEqual ( [ { name : 'MY_DO' , className : 'MyDurableObject' } ] ) ;
42+ expect ( result ! . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
43+ expect ( result ! . durableObjects ) . toEqual ( [ { name : 'MY_DO' , className : 'MyDurableObject' } ] ) ;
3244 } ) ;
3345
3446 it ( 'parses wrangler.json' , ( ) => {
@@ -43,8 +55,8 @@ describe('resolveWranglerConfig', () => {
4355
4456 const result = resolveWranglerConfig ( dir ) ;
4557 expect ( result ) . toBeDefined ( ) ;
46- expect ( result ! . config . main ) . toBe ( join ( dir , 'src/worker.ts' ) ) ;
47- expect ( result ! . config . durableObjects ) . toEqual ( [ { name : 'DO_A' , className : 'A' } ] ) ;
58+ expect ( result ! . main ) . toBe ( join ( dir , 'src/worker.ts' ) ) ;
59+ expect ( result ! . durableObjects ) . toEqual ( [ { name : 'DO_A' , className : 'A' } ] ) ;
4860 } ) ;
4961
5062 it ( 'parses wrangler.jsonc (strips comments)' , ( ) => {
@@ -65,8 +77,8 @@ describe('resolveWranglerConfig', () => {
6577
6678 const result = resolveWranglerConfig ( dir ) ;
6779 expect ( result ) . toBeDefined ( ) ;
68- expect ( result ! . config . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
69- expect ( result ! . config . durableObjects ) . toEqual ( [ { name : 'DO' , className : 'MyDO' } ] ) ;
80+ expect ( result ! . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
81+ expect ( result ! . durableObjects ) . toEqual ( [ { name : 'DO' , className : 'MyDO' } ] ) ;
7082 } ) ;
7183
7284 it ( 'parses JSONC with trailing commas' , ( ) => {
@@ -84,15 +96,15 @@ describe('resolveWranglerConfig', () => {
8496 } ) ;
8597
8698 const result = resolveWranglerConfig ( dir ) ;
87- expect ( result ! . config . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
88- expect ( result ! . config . durableObjects ) . toEqual ( [ { name : 'DO' , className : 'MyDO' } ] ) ;
99+ expect ( result ! . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
100+ expect ( result ! . durableObjects ) . toEqual ( [ { name : 'DO' , className : 'MyDO' } ] ) ;
89101 } ) ;
90102
91103 it ( 'parses TOML single-quoted (literal) strings' , ( ) => {
92104 const dir = writeTempDir ( { 'wrangler.toml' : "main = 'src/index.ts'" } ) ;
93105
94106 const result = resolveWranglerConfig ( dir ) ;
95- expect ( result ! . config . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
107+ expect ( result ! . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
96108 } ) ;
97109
98110 it ( 'prefers wrangler.json over wrangler.toml (matching wrangler itself)' , ( ) => {
@@ -102,7 +114,7 @@ describe('resolveWranglerConfig', () => {
102114 } ) ;
103115
104116 const result = resolveWranglerConfig ( dir ) ;
105- expect ( result ! . config . main ) . toBe ( join ( dir , 'from-json.ts' ) ) ;
117+ expect ( result ! . main ) . toBe ( join ( dir , 'from-json.ts' ) ) ;
106118 } ) ;
107119
108120 it ( 'prefers wrangler.jsonc over wrangler.toml (matching wrangler itself)' , ( ) => {
@@ -112,7 +124,7 @@ describe('resolveWranglerConfig', () => {
112124 } ) ;
113125
114126 const result = resolveWranglerConfig ( dir ) ;
115- expect ( result ! . config . main ) . toBe ( join ( dir , 'from-jsonc.ts' ) ) ;
127+ expect ( result ! . main ) . toBe ( join ( dir , 'from-jsonc.ts' ) ) ;
116128 } ) ;
117129
118130 it ( 'handles TOML with commented-out bindings' , ( ) => {
@@ -131,7 +143,7 @@ describe('resolveWranglerConfig', () => {
131143 } ) ;
132144
133145 const result = resolveWranglerConfig ( dir ) ;
134- expect ( result ! . config . durableObjects ) . toEqual ( [ { name : 'REAL' , className : 'RealDO' } ] ) ;
146+ expect ( result ! . durableObjects ) . toEqual ( [ { name : 'REAL' , className : 'RealDO' } ] ) ;
135147 } ) ;
136148
137149 it ( 'handles multiple DO bindings' , ( ) => {
@@ -150,14 +162,16 @@ describe('resolveWranglerConfig', () => {
150162 } ) ;
151163
152164 const result = resolveWranglerConfig ( dir ) ;
153- expect ( result ! . config . durableObjects ) . toHaveLength ( 2 ) ;
154- expect ( result ! . config . durableObjects [ 0 ] ) . toEqual ( { name : 'DO_A' , className : 'A' } ) ;
155- expect ( result ! . config . durableObjects [ 1 ] ) . toEqual ( { name : 'DO_B' , className : 'B' } ) ;
165+ expect ( result ! . durableObjects ) . toHaveLength ( 2 ) ;
166+ expect ( result ! . durableObjects [ 0 ] ) . toEqual ( { name : 'DO_A' , className : 'A' } ) ;
167+ expect ( result ! . durableObjects [ 1 ] ) . toEqual ( { name : 'DO_B' , className : 'B' } ) ;
156168 } ) ;
157169
158- it ( 'returns undefined when no config exists' , ( ) => {
170+ it ( 'returns an empty config when no config file is found' , ( ) => {
171+ // With nothing to discover, wrangler yields a default (empty) config rather
172+ // than throwing, so the result is defined but carries no entry or bindings.
159173 const dir = writeTempDir ( { } ) ;
160- expect ( resolveWranglerConfig ( dir ) ) . toBeUndefined ( ) ;
174+ expect ( resolveWranglerConfig ( dir ) ) . toEqual ( { main : undefined , durableObjects : [ ] } ) ;
161175 } ) ;
162176
163177 it ( 'returns undefined for explicit non-existent path' , ( ) => {
@@ -169,8 +183,7 @@ describe('resolveWranglerConfig', () => {
169183
170184 const result = resolveWranglerConfig ( dir , 'custom.toml' ) ;
171185 expect ( result ) . toBeDefined ( ) ;
172- expect ( result ! . config . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
173- expect ( result ! . configDir ) . toBe ( dir ) ;
186+ expect ( result ! . main ) . toBe ( join ( dir , 'src/index.ts' ) ) ;
174187 } ) ;
175188
176189 it ( 'returns undefined for an empty config file instead of crashing' , ( ) => {
@@ -197,7 +210,7 @@ describe('resolveWranglerConfig', () => {
197210 } ) ;
198211
199212 const result = resolveWranglerConfig ( dir ) ;
200- expect ( result ! . config . durableObjects ) . toEqual ( [ { name : 'LOCAL' , className : 'LocalDO' } ] ) ;
213+ expect ( result ! . durableObjects ) . toEqual ( [ { name : 'LOCAL' , className : 'LocalDO' } ] ) ;
201214 } ) ;
202215
203216 it ( 'uses only the active environment DO bindings (does not union across envs)' , ( ) => {
@@ -219,7 +232,7 @@ describe('resolveWranglerConfig', () => {
219232 } ) ;
220233
221234 const result = resolveWranglerConfig ( dir ) ;
222- expect ( result ! . config . durableObjects ) . toEqual ( [ { name : 'TOP' , className : 'TopDO' } ] ) ;
235+ expect ( result ! . durableObjects ) . toEqual ( [ { name : 'TOP' , className : 'TopDO' } ] ) ;
223236 } ) ;
224237
225238 it ( 'honors CLOUDFLARE_ENV for both main and DO bindings' , ( ) => {
@@ -240,11 +253,87 @@ describe('resolveWranglerConfig', () => {
240253 process . env . CLOUDFLARE_ENV = 'staging' ;
241254 try {
242255 const result = resolveWranglerConfig ( dir ) ! ;
243- expect ( result . config . main ) . toBe ( join ( dir , 'src/staging.ts' ) ) ;
244- expect ( result . config . durableObjects ) . toEqual ( [ { name : 'STAGING_DO' , className : 'StagingDO' } ] ) ;
256+ expect ( result . main ) . toBe ( join ( dir , 'src/staging.ts' ) ) ;
257+ expect ( result . durableObjects ) . toEqual ( [ { name : 'STAGING_DO' , className : 'StagingDO' } ] ) ;
245258 } finally {
246259 if ( previous === undefined ) delete process . env . CLOUDFLARE_ENV ;
247260 else process . env . CLOUDFLARE_ENV = previous ;
248261 }
249262 } ) ;
250263} ) ;
264+
265+ // ---------------------------------------------------------------------------
266+ // What `unstable_readConfig` exposes about service-binding `entrypoint`.
267+ //
268+ // These characterize the wrangler API directly (not our wrapper) to justify a
269+ // design decision: a service binding's `entrypoint` names a *named export on
270+ // the target worker being bound to*, not an entrypoint this worker exposes.
271+ // So it cannot, in general, tell auto-wrap which of *this* worker's exports is
272+ // a handler — with one exception: a self-binding (`service === own name`).
273+ // ---------------------------------------------------------------------------
274+
275+ describe ( 'unstable_readConfig: service-binding entrypoint semantics' , ( ) => {
276+ function readConfig ( files : Record < string , string > ) {
277+ const dir = writeTempDir ( files ) ;
278+ return unstable_readConfig ( { config : join ( dir , Object . keys ( files ) [ 0 ] ! ) } , { hideWarnings : true } ) ;
279+ }
280+
281+ it ( 'resolves `main` to an absolute path' , ( ) => {
282+ const raw = readConfig ( {
283+ 'wrangler.json' : JSON . stringify ( { main : 'src/index.ts' , compatibility_date : '2024-01-01' } ) ,
284+ } ) ;
285+ // Not the literal `src/index.ts` from the file — wrangler resolves it.
286+ expect ( raw . main ) . not . toBe ( 'src/index.ts' ) ;
287+ expect ( raw . main ?. endsWith ( join ( 'src' , 'index.ts' ) ) ) . toBe ( true ) ;
288+ } ) ;
289+
290+ it ( "an outward service binding names the *target* worker's export, not ours" , ( ) => {
291+ const raw = readConfig ( {
292+ 'wrangler.json' : JSON . stringify ( {
293+ name : 'worker-a' ,
294+ main : 'src/index.ts' ,
295+ compatibility_date : '2024-01-01' ,
296+ services : [ { binding : 'MY_SVC' , service : 'worker-b' , entrypoint : 'SomeEntry' } ] ,
297+ } ) ,
298+ } ) ;
299+
300+ expect ( raw . name ) . toBe ( 'worker-a' ) ;
301+ // `entrypoint` belongs to `worker-b`, a different worker this build isn't
302+ // compiling — nothing in *our* entry file to wrap from this.
303+ expect ( raw . services ) . toEqual ( [ { binding : 'MY_SVC' , service : 'worker-b' , entrypoint : 'SomeEntry' } ] ) ;
304+ expect ( raw . services ?. [ 0 ] ?. service ) . not . toBe ( raw . name ) ;
305+ } ) ;
306+
307+ it ( 'a self-binding (service === own name) does name one of *our* exports' , ( ) => {
308+ const raw = readConfig ( {
309+ 'wrangler.json' : JSON . stringify ( {
310+ name : 'worker-self' ,
311+ main : 'src/index.ts' ,
312+ compatibility_date : '2024-01-01' ,
313+ services : [
314+ { binding : 'SELF' , service : 'worker-self' , entrypoint : 'InternalEntry' } ,
315+ { binding : 'OTHER' , service : 'worker-x' , entrypoint : 'RemoteEntry' } ,
316+ ] ,
317+ } ) ,
318+ } ) ;
319+
320+ // Only the self-bound entrypoint is ours; the other points at `worker-x`.
321+ const ownEntrypoints = ( raw . services ?? [ ] ) . filter ( s => s . service === raw . name ) . map ( s => s . entrypoint ) ;
322+ expect ( ownEntrypoints ) . toEqual ( [ 'InternalEntry' ] ) ;
323+ } ) ;
324+
325+ it ( 'leaves `name` undefined when the config omits it (no self-binding is derivable)' , ( ) => {
326+ const raw = readConfig ( {
327+ 'wrangler.json' : JSON . stringify ( {
328+ main : 'src/index.ts' ,
329+ compatibility_date : '2024-01-01' ,
330+ services : [ { binding : 'S' , service : 'x' , entrypoint : 'E' } ] ,
331+ } ) ,
332+ } ) ;
333+
334+ // Without a worker name there is no `service === name` to match against, so
335+ // even self-bindings can't be identified.
336+ expect ( raw . name ) . toBeUndefined ( ) ;
337+ expect ( raw . topLevelName ) . toBeUndefined ( ) ;
338+ } ) ;
339+ } ) ;
0 commit comments