@pathmx/core 0.5.0 → 0.5.2

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/server/http.ts CHANGED
@@ -216,7 +216,7 @@ async function dispatchRequest(
216
216
  function serveGenerated(app: Engine, pathname: string, head: boolean) {
217
217
  if (!pathname.startsWith("/.pmx/")) return
218
218
  const asset = app.servedAsset(pathname)
219
- if (!asset) return
219
+ if (asset?.type !== "generated") return
220
220
  return new Response(head ? null : asset.body, {
221
221
  headers: {
222
222
  "Content-Type": asset.contentType,
@@ -248,6 +248,15 @@ async function dispatch(
248
248
  })
249
249
  }
250
250
 
251
+ const authored = serveAuthored(
252
+ app,
253
+ url.pathname,
254
+ req.method === "HEAD",
255
+ session,
256
+ principal,
257
+ )
258
+ if (authored) return authored
259
+
251
260
  const route = resolveRoute(app.repo, url.pathname)
252
261
  if (route?.type === "source") {
253
262
  return serveSource(
@@ -292,6 +301,39 @@ async function dispatch(
292
301
  )
293
302
  }
294
303
 
304
+ function decodePathname(pathname: string) {
305
+ try {
306
+ return decodeURIComponent(pathname)
307
+ } catch {
308
+ return
309
+ }
310
+ }
311
+
312
+ function serveAuthored(
313
+ app: Engine,
314
+ pathname: string,
315
+ head: boolean,
316
+ session: CredentialSession,
317
+ principal: Principal,
318
+ ) {
319
+ const path = decodePathname(pathname)
320
+ if (!path) return
321
+ const asset = app.servedAsset(path)
322
+ if (asset?.type !== "authored") return
323
+ const access = app.readAccess(principal, asset.sourcePath)
324
+ if (access === "denied") return pageNotFound()
325
+ return withReadCache(
326
+ new Response(head ? null : asset.body, {
327
+ headers: {
328
+ "Content-Type": asset.contentType,
329
+ "Cache-Control": "public, max-age=31536000, immutable",
330
+ },
331
+ }),
332
+ access,
333
+ session,
334
+ )
335
+ }
336
+
295
337
  function serveSource(
296
338
  app: Engine,
297
339
  head: boolean,
@@ -317,17 +359,19 @@ async function serveAsset(
317
359
  session: CredentialSession,
318
360
  principal: Principal,
319
361
  ) {
320
- let path: string
321
- try {
322
- path = decodeURIComponent(pathname)
323
- } catch {
324
- return
325
- }
362
+ const path = decodePathname(pathname)
363
+ if (!path) return
326
364
  if (path.includes("..")) return
327
365
  const storagePath = app.repo.storagePath(path)
328
366
  if (!storagePath || !(await app.repo.isFile(path))) return
329
367
  const access = app.readAccess(principal, path)
330
368
  if (access === "denied") return pageNotFound()
331
369
  const body = head ? null : await app.environment.storage.readBlob(storagePath)
332
- return withReadCache(new Response(body), access, session)
370
+ return withReadCache(
371
+ new Response(body, {
372
+ headers: { "Cache-Control": "public, max-age=0, must-revalidate" },
373
+ }),
374
+ access,
375
+ session,
376
+ )
333
377
  }
package/server/watch.ts CHANGED
@@ -3,6 +3,7 @@ import { realpathSync } from "node:fs"
3
3
  import { relative } from "node:path"
4
4
  import type { Engine } from "../host/engine.ts"
5
5
  import { SourceDiagnosticError } from "../source-diagnostic.ts"
6
+ import { isAuthoredStoragePath } from "../storage-path.ts"
6
7
  import { CssImportIndex } from "./css-imports.ts"
7
8
 
8
9
  type UnresolvedLink = {
@@ -91,7 +92,13 @@ export function watchPaths(app: Engine, root: string) {
91
92
  const changedStyles = new Set<string>()
92
93
  for (const event of events) {
93
94
  const rel = relative(base, event.path)
94
- if (!rel || rel.startsWith("..")) continue
95
+ if (
96
+ !rel ||
97
+ rel.startsWith("..") ||
98
+ !isAuthoredStoragePath(rel)
99
+ ) {
100
+ continue
101
+ }
95
102
  app.log.debug("watch", { file: rel, type: event.type })
96
103
  if (rel.endsWith(".md")) await app.syncFromStorage(rel)
97
104
  else if (rel.endsWith(".css")) {
package/source/source.ts CHANGED
@@ -49,7 +49,6 @@ export function sourceIdFromPath(path: string, type = DEFAULT_SOURCE_TYPE) {
49
49
  }
50
50
 
51
51
  export function resolveSourceId(path: string, data: Topmatter) {
52
- if (typeof data.id === "string" && data.id.trim()) return data.id.trim()
53
52
  const type =
54
53
  typeof data.type === "string" && data.type.trim()
55
54
  ? data.type.trim()
@@ -0,0 +1,8 @@
1
+ const CONTROL_DIRECTORIES = new Set([".git", ".pathmx", "node_modules"])
2
+
3
+ /** True when a storage-relative path belongs to authored project content. */
4
+ export function isAuthoredStoragePath(path: string) {
5
+ return path
6
+ .split(/[\\/]/)
7
+ .every((segment) => !CONTROL_DIRECTORIES.has(segment))
8
+ }