@pikku/bun-server 0.12.8 → 0.12.10

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/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # @pikku/bun-server
2
2
 
3
+ ## 0.12.10
4
+
5
+ ### Patch Changes
6
+
7
+ - 80eb5c0: Generate a desktop shell from `pikku deploy apply --desktop`
8
+
9
+ `pikku deploy apply --provider standalone --runtime bun --desktop` now emits a
10
+ `src-tauri/` crate that ships the compiled binary as a sidecar and opens a
11
+ window on the server's own HTTP origin, so cookies, CORS and OAuth behave
12
+ exactly as they do in a browser. Regeneration is idempotent and leaves an
13
+ edited file alone rather than overwriting it.
14
+
15
+ `--desktop-url https://app.example.com` builds the other shape: a shell that
16
+ points at an already-deployed server. Nothing is bundled — no sidecar, no
17
+ binary, and so no bun runtime to compile one — and the window is declared in
18
+ `tauri.conf.json` rather than opened from Rust, because the origin is known up
19
+ front. The url can also live in `pikku.config.json` as `deploy.desktop.url`,
20
+ alongside `deploy.desktop.identifier`.
21
+
22
+ Supporting changes: `SERVER_READY_MARKER` moved to `@pikku/deploy` (the CLI
23
+ re-exports it from its old path), both HTTP runtimes expose the port they
24
+ actually bound so `--port 0` reports a real port, and the generated server
25
+ entry exits when its parent process goes away.
26
+
27
+ - 80eb5c0: feat: serve a built frontend from the pikku server's own origin
28
+
29
+ A new `frontend` key in `pikku.config.json` names a directory of built
30
+ frontend output. `pikku serve` mounts it, and `pikku deploy` ships it inside
31
+ the distributable — into a directory beside the bundle for the node runtime,
32
+ and embedded in the binary for a `bun build --compile` standalone. `pikku dev`
33
+ deliberately ignores it and says so, because the frontend's own dev server owns
34
+ that job.
35
+
36
+ Pikku reads the frontend's output and never builds it, so an unbuilt directory
37
+ fails with a message that says which build to run rather than booting a server
38
+ that answers every page with a 404.
39
+
40
+ - Updated dependencies [80eb5c0]
41
+ - Updated dependencies [2252016]
42
+ - @pikku/core@0.12.98
43
+
44
+ ## 0.12.9
45
+
46
+ ### Patch Changes
47
+
48
+ - 6ff72d3: Raise the supported Bun version to 1.4.
49
+
50
+ `@pikku/bun-server` and `@pikku/kysely-bun-sqlite` now declare `engines.bun: >=1.4.0`
51
+ and build against `@types/bun@^1.4.0`. `create-pikku` scaffolds
52
+ `"packageManager": "bun@1.4.0"`, and the fabric `smoke`/`validate` commands default to
53
+ and recommend the same version. CI pins `oven-sh/setup-bun` to 1.4.0 instead of
54
+ tracking `latest`.
55
+
3
56
  ## 0.12.8
4
57
 
5
58
  ### Patch Changes
@@ -7,6 +7,7 @@ export type StaticMount = {
7
7
  urlPrefix: string;
8
8
  directory: string;
9
9
  spaFallback?: boolean;
10
+ assets?: Record<string, string>;
10
11
  };
11
12
  export type BunServerConfig = CoreConfig & {
12
13
  port: number;
@@ -40,7 +41,10 @@ export declare class PikkuBunServer {
40
41
  private initMCP;
41
42
  start(): Promise<void>;
42
43
  get port(): number;
43
- private serveStaticMounts;
44
+ private matchesPrefix;
45
+ private staticMountCandidates;
46
+ private serveStaticFiles;
47
+ private serveSpaFallback;
44
48
  private resolveStaticFile;
45
49
  stop(): Promise<void>;
46
50
  enableExitOnSignals(): void;
@@ -118,7 +118,7 @@ export class PikkuBunServer {
118
118
  if (contentResponse) {
119
119
  return contentResponse;
120
120
  }
121
- const staticResponse = await this.serveStaticMounts(req);
121
+ const staticResponse = await this.serveStaticFiles(req);
122
122
  if (staticResponse) {
123
123
  return staticResponse;
124
124
  }
@@ -128,7 +128,14 @@ export class PikkuBunServer {
128
128
  respondWith404: true,
129
129
  ...options,
130
130
  });
131
- return pikkuRes.toResponse();
131
+ const response = pikkuRes.toResponse();
132
+ if (response.status === 404) {
133
+ const fallback = await this.serveSpaFallback(req);
134
+ if (fallback) {
135
+ return fallback;
136
+ }
137
+ }
138
+ return response;
132
139
  },
133
140
  websocket: {
134
141
  open: (ws) => {
@@ -169,48 +176,82 @@ export class PikkuBunServer {
169
176
  },
170
177
  });
171
178
  eventHub.setServer(this.server);
172
- logger.info(`pikku-bun-server: listening on http://${config.hostname ?? 'localhost'}:${config.port}`);
179
+ logger.info(`pikku-bun-server: listening on http://${config.hostname ?? 'localhost'}:${this.port}`);
173
180
  }
174
181
  get port() {
175
182
  return this.server?.port ?? this.config.port;
176
183
  }
177
- async serveStaticMounts(req) {
184
+ matchesPrefix(pathname, prefix) {
185
+ if (prefix === '' || prefix === '/') {
186
+ return true;
187
+ }
188
+ return pathname === prefix || pathname.startsWith(`${prefix}/`);
189
+ }
190
+ staticMountCandidates(req) {
178
191
  const mounts = this.config.staticMounts;
179
192
  if (!mounts?.length || (req.method !== 'GET' && req.method !== 'HEAD')) {
180
193
  return null;
181
194
  }
182
195
  const pathname = decodeURIComponent(new URL(req.url).pathname);
183
- for (const mount of mounts) {
184
- if (pathname !== mount.urlPrefix &&
185
- !pathname.startsWith(`${mount.urlPrefix}/`)) {
186
- continue;
196
+ return {
197
+ pathname,
198
+ mounts: mounts.filter((mount) => this.matchesPrefix(pathname, mount.urlPrefix)),
199
+ };
200
+ }
201
+ async serveStaticFiles(req) {
202
+ const candidates = this.staticMountCandidates(req);
203
+ if (!candidates) {
204
+ return null;
205
+ }
206
+ for (const mount of candidates.mounts) {
207
+ const key = candidates.pathname
208
+ .slice(mount.urlPrefix.length)
209
+ .replace(/^\/+/, '');
210
+ const result = await this.resolveStaticFile(mount, key);
211
+ if (result === 'rejected') {
212
+ return new Response('Not Found', { status: 404 });
187
213
  }
188
- const key = pathname.slice(mount.urlPrefix.length).replace(/^\/+/, '');
189
- const file = await this.resolveStaticFile(mount, key);
190
- if (file) {
191
- return new Response(req.method === 'HEAD' ? null : file);
214
+ if (result) {
215
+ return new Response(req.method === 'HEAD' ? null : result);
192
216
  }
193
- if (mount.spaFallback) {
194
- const index = await this.resolveStaticFile(mount, 'index.html');
195
- if (index) {
196
- return new Response(req.method === 'HEAD' ? null : index);
197
- }
217
+ }
218
+ return null;
219
+ }
220
+ async serveSpaFallback(req) {
221
+ const candidates = this.staticMountCandidates(req);
222
+ if (!candidates) {
223
+ return null;
224
+ }
225
+ for (const mount of candidates.mounts) {
226
+ if (!mount.spaFallback) {
227
+ continue;
228
+ }
229
+ const index = await this.resolveStaticFile(mount, 'index.html');
230
+ if (index && index !== 'rejected') {
231
+ return new Response(req.method === 'HEAD' ? null : index);
198
232
  }
199
- return new Response('Not Found', { status: 404 });
200
233
  }
201
234
  return null;
202
235
  }
203
236
  async resolveStaticFile(mount, key) {
237
+ if (mount.assets) {
238
+ const assetPath = mount.assets[key === '' ? 'index.html' : key];
239
+ if (!assetPath) {
240
+ return null;
241
+ }
242
+ const asset = Bun.file(assetPath);
243
+ return (await asset.exists()) ? asset : null;
244
+ }
204
245
  const directory = resolve(mount.directory);
205
246
  const targetPath = key === '' ? resolve(directory, 'index.html') : resolve(directory, key);
206
247
  if (targetPath !== directory && !targetPath.startsWith(`${directory}/`)) {
207
- return null;
248
+ return 'rejected';
208
249
  }
209
250
  let file = Bun.file(targetPath);
210
251
  if (!(await file.exists())) {
211
252
  const indexPath = resolve(targetPath, 'index.html');
212
253
  if (!indexPath.startsWith(`${directory}/`)) {
213
- return null;
254
+ return 'rejected';
214
255
  }
215
256
  file = Bun.file(indexPath);
216
257
  if (!(await file.exists())) {
@@ -1 +1 @@
1
- {"version":3,"file":"pikku-bun-server.js","sourceRoot":"","sources":["../src/pikku-bun-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pikku-bun-server.js","sourceRoot":"","sources":["../src/pikku-bun-server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/bun-server",
3
- "version": "0.12.8",
3
+ "version": "0.12.10",
4
4
  "description": "Pikku server adapter for Bun (Bun.serve)",
5
5
  "license": "MIT",
6
6
  "author": "yasser.fadl@gmail.com",
@@ -16,7 +16,7 @@
16
16
  "prepublishOnly": "bun run build"
17
17
  },
18
18
  "peerDependencies": {
19
- "@pikku/core": "^0.12.85",
19
+ "@pikku/core": "^0.12.98",
20
20
  "@pikku/modelcontextprotocol": "^0.12.10"
21
21
  },
22
22
  "peerDependenciesMeta": {
@@ -25,12 +25,12 @@
25
25
  }
26
26
  },
27
27
  "devDependencies": {
28
- "@pikku/core": "^0.12.85",
28
+ "@pikku/core": "^0.12.98",
29
29
  "@pikku/modelcontextprotocol": "^0.12.10",
30
- "@types/bun": "latest",
30
+ "@types/bun": "^1.4.0",
31
31
  "typescript": "^6.0.3"
32
32
  },
33
33
  "engines": {
34
- "bun": ">=1.0.0"
34
+ "bun": ">=1.4.0"
35
35
  }
36
36
  }
@@ -31,6 +31,17 @@ export type StaticMount = {
31
31
  urlPrefix: string
32
32
  directory: string
33
33
  spaFallback?: boolean
34
+ /**
35
+ * Serve from an explicit key → path map instead of from `directory`, which is
36
+ * then unused. Keys are mount-relative request paths (`index.html`,
37
+ * `assets/app-a1b2c3.js`).
38
+ *
39
+ * This exists for assets that are not laid out as a directory at all: a
40
+ * `bun build --compile` binary embeds each file and hands back an opaque
41
+ * `/$bunfs/…` path, so the only way to find one is to have recorded it at
42
+ * build time.
43
+ */
44
+ assets?: Record<string, string>
34
45
  }
35
46
 
36
47
  export type BunServerConfig = CoreConfig & {
@@ -220,7 +231,7 @@ export class PikkuBunServer {
220
231
  return contentResponse
221
232
  }
222
233
 
223
- const staticResponse = await this.serveStaticMounts(req)
234
+ const staticResponse = await this.serveStaticFiles(req)
224
235
  if (staticResponse) {
225
236
  return staticResponse
226
237
  }
@@ -231,7 +242,14 @@ export class PikkuBunServer {
231
242
  respondWith404: true,
232
243
  ...options,
233
244
  })
234
- return pikkuRes.toResponse()
245
+ const response = pikkuRes.toResponse()
246
+ if (response.status === 404) {
247
+ const fallback = await this.serveSpaFallback(req)
248
+ if (fallback) {
249
+ return fallback
250
+ }
251
+ }
252
+ return response
235
253
  },
236
254
 
237
255
  websocket: {
@@ -279,60 +297,133 @@ export class PikkuBunServer {
279
297
 
280
298
  eventHub.setServer(this.server as BunServer<unknown>)
281
299
  logger.info(
282
- `pikku-bun-server: listening on http://${config.hostname ?? 'localhost'}:${config.port}`
300
+ `pikku-bun-server: listening on http://${config.hostname ?? 'localhost'}:${this.port}`
283
301
  )
284
302
  }
285
303
 
304
+ /**
305
+ * The port the server is actually listening on.
306
+ *
307
+ * Not the same as `config.port` whenever that is `0`: the OS picks a free
308
+ * port at bind time, and a parent process that was told `0` has no other way
309
+ * to find out which one. Falls back to the requested port before `start()`.
310
+ */
286
311
  public get port(): number {
287
312
  return this.server?.port ?? this.config.port
288
313
  }
289
314
 
290
- private async serveStaticMounts(req: Request): Promise<Response | null> {
315
+ private matchesPrefix(pathname: string, prefix: string): boolean {
316
+ // A root mount covers the whole tree; the generic test below would match
317
+ // only `/` itself, since nothing starts with `//`.
318
+ if (prefix === '' || prefix === '/') {
319
+ return true
320
+ }
321
+ return pathname === prefix || pathname.startsWith(`${prefix}/`)
322
+ }
323
+
324
+ /**
325
+ * Resolve the mounts a GET/HEAD request could be served from, or null when
326
+ * static serving does not apply to this request at all.
327
+ */
328
+ private staticMountCandidates(
329
+ req: Request
330
+ ): { pathname: string; mounts: StaticMount[] } | null {
291
331
  const mounts = this.config.staticMounts
292
332
  if (!mounts?.length || (req.method !== 'GET' && req.method !== 'HEAD')) {
293
333
  return null
294
334
  }
295
335
  const pathname = decodeURIComponent(new URL(req.url).pathname)
336
+ return {
337
+ pathname,
338
+ mounts: mounts.filter((mount) =>
339
+ this.matchesPrefix(pathname, mount.urlPrefix)
340
+ ),
341
+ }
342
+ }
296
343
 
297
- for (const mount of mounts) {
298
- if (
299
- pathname !== mount.urlPrefix &&
300
- !pathname.startsWith(`${mount.urlPrefix}/`)
301
- ) {
302
- continue
344
+ /**
345
+ * Serve an exact file hit only. A miss falls through to route dispatch, so a
346
+ * mount at `/` cannot swallow the API — the app shell is served afterwards by
347
+ * `serveSpaFallback`, once dispatch has had its chance and produced a 404.
348
+ */
349
+ private async serveStaticFiles(req: Request): Promise<Response | null> {
350
+ const candidates = this.staticMountCandidates(req)
351
+ if (!candidates) {
352
+ return null
353
+ }
354
+
355
+ for (const mount of candidates.mounts) {
356
+ const key = candidates.pathname
357
+ .slice(mount.urlPrefix.length)
358
+ .replace(/^\/+/, '')
359
+ const result = await this.resolveStaticFile(mount, key)
360
+ if (result === 'rejected') {
361
+ // A key that escaped the mount directory is refused outright rather
362
+ // than falling through, which would answer a traversal attempt with a
363
+ // 200 and the app shell.
364
+ return new Response('Not Found', { status: 404 })
303
365
  }
304
- const key = pathname.slice(mount.urlPrefix.length).replace(/^\/+/, '')
305
- const file = await this.resolveStaticFile(mount, key)
306
- if (file) {
307
- return new Response(req.method === 'HEAD' ? null : file)
366
+ if (result) {
367
+ return new Response(req.method === 'HEAD' ? null : result)
308
368
  }
309
- if (mount.spaFallback) {
310
- const index = await this.resolveStaticFile(mount, 'index.html')
311
- if (index) {
312
- return new Response(req.method === 'HEAD' ? null : index)
313
- }
369
+ }
370
+
371
+ return null
372
+ }
373
+
374
+ /**
375
+ * Last resort for a client-side route: dispatch has already 404'd, so an
376
+ * unmatched path under a `spaFallback` mount is the SPA's own routing.
377
+ */
378
+ private async serveSpaFallback(req: Request): Promise<Response | null> {
379
+ const candidates = this.staticMountCandidates(req)
380
+ if (!candidates) {
381
+ return null
382
+ }
383
+
384
+ for (const mount of candidates.mounts) {
385
+ if (!mount.spaFallback) {
386
+ continue
387
+ }
388
+ const index = await this.resolveStaticFile(mount, 'index.html')
389
+ if (index && index !== 'rejected') {
390
+ return new Response(req.method === 'HEAD' ? null : index)
314
391
  }
315
- return new Response('Not Found', { status: 404 })
316
392
  }
317
393
 
318
394
  return null
319
395
  }
320
396
 
397
+ /**
398
+ * `'rejected'` is kept distinct from `null` because the two must not share a
399
+ * fate: a missing file is a candidate for the SPA fallback, while a key that
400
+ * resolved outside the mount directory must never be. An `assets` mount never
401
+ * produces `'rejected'` at all — a map lookup has no directory to escape.
402
+ */
321
403
  private async resolveStaticFile(
322
404
  mount: StaticMount,
323
405
  key: string
324
- ): Promise<ReturnType<typeof Bun.file> | null> {
406
+ ): Promise<ReturnType<typeof Bun.file> | 'rejected' | null> {
407
+ if (mount.assets) {
408
+ const assetPath = mount.assets[key === '' ? 'index.html' : key]
409
+ if (!assetPath) {
410
+ return null
411
+ }
412
+ const asset = Bun.file(assetPath)
413
+ return (await asset.exists()) ? asset : null
414
+ }
415
+
325
416
  const directory = resolve(mount.directory)
326
417
  const targetPath =
327
418
  key === '' ? resolve(directory, 'index.html') : resolve(directory, key)
328
419
  if (targetPath !== directory && !targetPath.startsWith(`${directory}/`)) {
329
- return null
420
+ return 'rejected'
330
421
  }
331
422
  let file = Bun.file(targetPath)
332
423
  if (!(await file.exists())) {
333
424
  const indexPath = resolve(targetPath, 'index.html')
334
425
  if (!indexPath.startsWith(`${directory}/`)) {
335
- return null
426
+ return 'rejected'
336
427
  }
337
428
  file = Bun.file(indexPath)
338
429
  if (!(await file.exists())) {
@@ -4,7 +4,9 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync } from 'node:fs'
4
4
  import { tmpdir } from 'node:os'
5
5
  import { join } from 'node:path'
6
6
  import { setSingletonServices } from '@pikku/core/state'
7
- import { resetPikkuState } from '@pikku/core/state'
7
+ import { pikkuState, resetPikkuState } from '@pikku/core/state'
8
+ import { httpRouter, wireHTTP } from '@pikku/core/http'
9
+ import { addFunction } from '@pikku/core/function'
8
10
  import type { Logger } from '@pikku/core/services'
9
11
  import { PikkuBunServer } from './pikku-bun-server.js'
10
12
 
@@ -106,3 +108,223 @@ describe('PikkuBunServer static mounts', () => {
106
108
  }
107
109
  })
108
110
  })
111
+
112
+ describe('PikkuBunServer root-mounted SPA', () => {
113
+ let server: PikkuBunServer
114
+ let tmpDir: string
115
+ let origin: string
116
+
117
+ before(async () => {
118
+ resetPikkuState()
119
+ httpRouter.reset()
120
+ setSingletonServices({
121
+ logger: noopLogger,
122
+ schema: {
123
+ compileSchema: () => {},
124
+ getSchemaNames: () => new Set<string>(),
125
+ },
126
+ } as any)
127
+ tmpDir = mkdtempSync(join(tmpdir(), 'pikku-bun-spa-'))
128
+ writeFileSync(
129
+ join(tmpDir, 'index.html'),
130
+ '<!doctype html><title>app shell</title>'
131
+ )
132
+ mkdirSync(join(tmpDir, 'assets'))
133
+ writeFileSync(join(tmpDir, 'assets', 'app.js'), 'console.log("app")')
134
+
135
+ pikkuState(null, 'function', 'meta', {
136
+ things_func: {
137
+ pikkuFuncId: 'things_func',
138
+ inputSchemaName: null,
139
+ outputSchemaName: null,
140
+ sessionless: true,
141
+ },
142
+ } as any)
143
+ pikkuState(null, 'http', 'meta', {
144
+ get: {
145
+ '/api/things': {
146
+ pikkuFuncId: 'things_func',
147
+ route: '/api/things',
148
+ method: 'get',
149
+ },
150
+ },
151
+ post: {},
152
+ delete: {},
153
+ patch: {},
154
+ head: {},
155
+ put: {},
156
+ options: {},
157
+ } as any)
158
+ addFunction('things_func', { func: async () => ({ things: ['one'] }) })
159
+ wireHTTP({
160
+ route: '/api/things',
161
+ method: 'get',
162
+ auth: false,
163
+ func: { func: async () => ({ things: ['one'] }) },
164
+ } as any)
165
+ httpRouter.initialize()
166
+
167
+ server = new PikkuBunServer(
168
+ {
169
+ port: 0,
170
+ hostname: '127.0.0.1',
171
+ staticMounts: [
172
+ { urlPrefix: '/', directory: tmpDir, spaFallback: true },
173
+ ],
174
+ } as any,
175
+ noopLogger
176
+ )
177
+ await server.init()
178
+ await server.start()
179
+ origin = `http://127.0.0.1:${server.port}`
180
+ })
181
+
182
+ after(async () => {
183
+ await server.stop()
184
+ rmSync(tmpDir, { recursive: true, force: true })
185
+ })
186
+
187
+ test('a wired API route still dispatches under a root SPA mount', async () => {
188
+ const response = await fetch(`${origin}/api/things`)
189
+ assert.equal(response.status, 200)
190
+ assert.match(
191
+ response.headers.get('content-type') ?? '',
192
+ /application\/json/,
193
+ 'API route returned the SPA shell instead of dispatching'
194
+ )
195
+ assert.deepEqual(await response.json(), { things: ['one'] })
196
+ })
197
+
198
+ test('static files still win over the SPA fallback', async () => {
199
+ const response = await fetch(`${origin}/assets/app.js`)
200
+ assert.equal(response.status, 200)
201
+ assert.equal(await response.text(), 'console.log("app")')
202
+ })
203
+
204
+ test('unknown client routes still fall back to the app shell', async () => {
205
+ const response = await fetch(`${origin}/records/42`)
206
+ assert.equal(response.status, 200)
207
+ assert.match(await response.text(), /<title>app shell<\/title>/)
208
+ })
209
+ })
210
+
211
+ describe('PikkuBunServer asset-mapped mount', () => {
212
+ let server: PikkuBunServer
213
+ let tmpDir: string
214
+ let origin: string
215
+
216
+ before(async () => {
217
+ resetPikkuState()
218
+ httpRouter.reset()
219
+ setSingletonServices({
220
+ logger: noopLogger,
221
+ schema: {
222
+ compileSchema: () => {},
223
+ getSchemaNames: () => new Set<string>(),
224
+ },
225
+ } as any)
226
+ tmpDir = mkdtempSync(join(tmpdir(), 'pikku-bun-assets-'))
227
+
228
+ // The files live in a `store` subdirectory that the mount's own `directory`
229
+ // does not contain, so a hit can only come from the map.
230
+ const store = join(tmpDir, 'store')
231
+ mkdirSync(store)
232
+ writeFileSync(
233
+ join(store, 'shell-abc123.html'),
234
+ '<!doctype html><title>embedded shell</title>'
235
+ )
236
+ writeFileSync(join(store, 'app-def456.js'), 'console.log("embedded")')
237
+
238
+ pikkuState(null, 'function', 'meta', {
239
+ things_func: {
240
+ pikkuFuncId: 'things_func',
241
+ inputSchemaName: null,
242
+ outputSchemaName: null,
243
+ sessionless: true,
244
+ },
245
+ } as any)
246
+ pikkuState(null, 'http', 'meta', {
247
+ get: {
248
+ '/api/things': {
249
+ pikkuFuncId: 'things_func',
250
+ route: '/api/things',
251
+ method: 'get',
252
+ },
253
+ },
254
+ post: {},
255
+ delete: {},
256
+ patch: {},
257
+ head: {},
258
+ put: {},
259
+ options: {},
260
+ } as any)
261
+ addFunction('things_func', { func: async () => ({ things: ['one'] }) })
262
+ wireHTTP({
263
+ route: '/api/things',
264
+ method: 'get',
265
+ auth: false,
266
+ func: { func: async () => ({ things: ['one'] }) },
267
+ } as any)
268
+ httpRouter.initialize()
269
+
270
+ server = new PikkuBunServer(
271
+ {
272
+ port: 0,
273
+ hostname: '127.0.0.1',
274
+ staticMounts: [
275
+ {
276
+ urlPrefix: '/',
277
+ directory: join(tmpDir, 'nonexistent'),
278
+ spaFallback: true,
279
+ assets: {
280
+ 'index.html': join(store, 'shell-abc123.html'),
281
+ 'assets/app.js': join(store, 'app-def456.js'),
282
+ },
283
+ },
284
+ ],
285
+ } as any,
286
+ noopLogger
287
+ )
288
+ await server.init()
289
+ await server.start()
290
+ origin = `http://127.0.0.1:${server.port}`
291
+ })
292
+
293
+ after(async () => {
294
+ await server.stop()
295
+ rmSync(tmpDir, { recursive: true, force: true })
296
+ })
297
+
298
+ test('serves a mapped asset from the path the map names', async () => {
299
+ const response = await fetch(`${origin}/assets/app.js`)
300
+ assert.equal(response.status, 200)
301
+ assert.match(response.headers.get('content-type') ?? '', /javascript/)
302
+ assert.equal(await response.text(), 'console.log("embedded")')
303
+ })
304
+
305
+ test('serves the mapped index.html at the mount root', async () => {
306
+ const response = await fetch(`${origin}/`)
307
+ assert.equal(response.status, 200)
308
+ assert.match(await response.text(), /<title>embedded shell<\/title>/)
309
+ })
310
+
311
+ test('a wired API route still dispatches under a root asset mount', async () => {
312
+ const response = await fetch(`${origin}/api/things`)
313
+ assert.equal(response.status, 200)
314
+ assert.deepEqual(await response.json(), { things: ['one'] })
315
+ })
316
+
317
+ test('an unmapped key falls back to the app shell', async () => {
318
+ const response = await fetch(`${origin}/records/42`)
319
+ assert.equal(response.status, 200)
320
+ assert.match(await response.text(), /<title>embedded shell<\/title>/)
321
+ })
322
+
323
+ test('a traversal-shaped key is a plain miss, not a rejection', async () => {
324
+ // A map lookup cannot escape a directory, so there is nothing to reject:
325
+ // the key is simply absent and the SPA fallback is allowed to answer.
326
+ const response = await fetch(`${origin}/%2e%2e/pikku-bun-assets-secret.txt`)
327
+ assert.equal(response.status, 200)
328
+ assert.match(await response.text(), /<title>embedded shell<\/title>/)
329
+ })
330
+ })