@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/README.md +9 -1
- package/assets.ts +116 -31
- package/cli/dev.ts +3 -2
- package/cli/lint.ts +50 -8
- package/cli/update.ts +47 -5
- package/dist/pathmx.js +2 -1223
- package/document.ts +15 -5
- package/environments/bun.ts +5 -3
- package/host/authored-assets.ts +98 -0
- package/host/engine.ts +45 -26
- package/host/shell.html +1 -0
- package/host/source-sync.ts +1 -1
- package/ops/href.ts +0 -11
- package/ops/move-source.ts +5 -3
- package/package.json +1 -1
- package/plugins/runtime/document.ts +23 -4
- package/plugins/runtime/runtime.css +6 -0
- package/plugins/runtime/start.ts +1 -0
- package/plugins/styles.ts +25 -24
- package/plugins/types.ts +1 -0
- package/publish/build.ts +15 -5
- package/server/http.ts +52 -8
- package/server/watch.ts +8 -1
- package/source/source.ts +0 -1
- package/storage-path.ts +8 -0
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 (
|
|
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
|
-
|
|
321
|
-
|
|
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(
|
|
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 (
|
|
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()
|
package/storage-path.ts
ADDED
|
@@ -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
|
+
}
|