@pathmx/core 0.5.0
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/LICENSE.md +172 -0
- package/README.md +111 -0
- package/actions/actions.ts +307 -0
- package/actions/draft.ts +105 -0
- package/actions/transaction.ts +164 -0
- package/assets.ts +149 -0
- package/authority/access.ts +820 -0
- package/authority/actors.ts +271 -0
- package/authority/agents.ts +95 -0
- package/authority/credentials.ts +144 -0
- package/authority/model.ts +71 -0
- package/authority/sharing.ts +89 -0
- package/browser-bundle.ts +61 -0
- package/canonical.ts +41 -0
- package/cli/dev.ts +134 -0
- package/cli/export.ts +74 -0
- package/cli/index.ts +52 -0
- package/cli/lint.ts +164 -0
- package/cli/mv.ts +30 -0
- package/cli/perms.ts +182 -0
- package/cli/plugin-packages.ts +159 -0
- package/cli/plugins.ts +95 -0
- package/cli/query.ts +225 -0
- package/cli/readme.md +95 -0
- package/cli/release.ts +75 -0
- package/cli/rm.ts +22 -0
- package/cli/serve.ts +100 -0
- package/cli/tree.ts +131 -0
- package/cli/update.ts +78 -0
- package/cli/utils.ts +53 -0
- package/core.ts +96 -0
- package/database.ts +189 -0
- package/dependencies.ts +60 -0
- package/dist/pathmx.js +1224 -0
- package/document.ts +194 -0
- package/environment.ts +110 -0
- package/environments/bun-plugins.ts +68 -0
- package/environments/bun-publish.ts +194 -0
- package/environments/bun.ts +223 -0
- package/globals.d.ts +14 -0
- package/graph.ts +153 -0
- package/host/compiler.ts +314 -0
- package/host/create.ts +66 -0
- package/host/engine.ts +630 -0
- package/host/plugin-host.ts +487 -0
- package/host/render.ts +102 -0
- package/host/response.ts +20 -0
- package/host/shell.html +15 -0
- package/host/source-sync.ts +254 -0
- package/html-attributes.ts +15 -0
- package/html.ts +48 -0
- package/icons/LICENSE +43 -0
- package/icons/lucide.ts +54 -0
- package/index.ts +75 -0
- package/invalidation.ts +85 -0
- package/log.ts +84 -0
- package/ops/href.ts +87 -0
- package/ops/index.ts +9 -0
- package/ops/move-source.ts +114 -0
- package/ops/plan.ts +47 -0
- package/ops/remove-source.ts +70 -0
- package/package.json +46 -0
- package/path.ts +75 -0
- package/pattern.ts +314 -0
- package/perf.ts +88 -0
- package/plugins/actor-path.ts +43 -0
- package/plugins/actor-profile.ts +55 -0
- package/plugins/agent-browsing.ts +183 -0
- package/plugins/app-component.ts +23 -0
- package/plugins/block-layout/index.ts +78 -0
- package/plugins/block-layout/theme.css +90 -0
- package/plugins/callout/index.ts +236 -0
- package/plugins/callout/theme.css +138 -0
- package/plugins/canonical-links.ts +144 -0
- package/plugins/code-highlight/client.ts +89 -0
- package/plugins/code-highlight/index.ts +147 -0
- package/plugins/code-highlight/theme.css +89 -0
- package/plugins/context.ts +131 -0
- package/plugins/datatype/Datatype[wdth,wght].woff2 +0 -0
- package/plugins/datatype/OFL.txt +96 -0
- package/plugins/datatype/index.ts +287 -0
- package/plugins/definition-list.ts +236 -0
- package/plugins/directive-definitions.ts +13 -0
- package/plugins/embedding/client.ts +88 -0
- package/plugins/embedding/index.ts +43 -0
- package/plugins/image/index.ts +68 -0
- package/plugins/image/theme.css +43 -0
- package/plugins/include.ts +119 -0
- package/plugins/index.ts +91 -0
- package/plugins/input/authoring.ts +236 -0
- package/plugins/input/config.ts +85 -0
- package/plugins/input/index.ts +359 -0
- package/plugins/input/model.ts +173 -0
- package/plugins/input/state.ts +272 -0
- package/plugins/layout.ts +313 -0
- package/plugins/literate/client.ts +208 -0
- package/plugins/literate/index.ts +742 -0
- package/plugins/math/index.ts +140 -0
- package/plugins/meta.ts +42 -0
- package/plugins/navigation/index.ts +158 -0
- package/plugins/navigation/model.ts +364 -0
- package/plugins/preset.ts +65 -0
- package/plugins/query/index.ts +322 -0
- package/plugins/rss/index.ts +46 -0
- package/plugins/runtime/actions.ts +62 -0
- package/plugins/runtime/client.ts +8 -0
- package/plugins/runtime/document.ts +506 -0
- package/plugins/runtime/error.ts +154 -0
- package/plugins/runtime/heading-links.ts +31 -0
- package/plugins/runtime/index.ts +104 -0
- package/plugins/runtime/live.ts +51 -0
- package/plugins/runtime/navigation.ts +113 -0
- package/plugins/runtime/pending.ts +53 -0
- package/plugins/runtime/publication.ts +19 -0
- package/plugins/runtime/start.ts +177 -0
- package/plugins/runtime/static-client.ts +6 -0
- package/plugins/runtime/static-live.ts +188 -0
- package/plugins/runtime/view-transition.ts +72 -0
- package/plugins/runtime/viewport.ts +110 -0
- package/plugins/schema.ts +24 -0
- package/plugins/scripts.ts +140 -0
- package/plugins/source-preview/client.ts +256 -0
- package/plugins/source-preview/index.ts +132 -0
- package/plugins/source-preview/model.ts +11 -0
- package/plugins/source-preview/preview.css +107 -0
- package/plugins/styles.ts +145 -0
- package/plugins/theme/components.ts +10 -0
- package/plugins/theme/foundation.css +358 -0
- package/plugins/theme/index.ts +333 -0
- package/plugins/theme/prose.css +284 -0
- package/plugins/theme/scheme.js +63 -0
- package/plugins/theme/toggle.css +44 -0
- package/plugins/theme/view-transition.css +68 -0
- package/plugins/toc/client.ts +146 -0
- package/plugins/toc/index.ts +232 -0
- package/plugins/toc/theme.css +172 -0
- package/plugins/types.ts +538 -0
- package/problem.ts +41 -0
- package/project.ts +6 -0
- package/publish/build.ts +402 -0
- package/publish/concurrent.ts +32 -0
- package/publish/identity.ts +63 -0
- package/publish/index.ts +11 -0
- package/publish/location.ts +167 -0
- package/publish/model.ts +58 -0
- package/publish/plan.ts +300 -0
- package/publish/validate.ts +150 -0
- package/query/candidate-index.ts +101 -0
- package/query/dependency-index.ts +99 -0
- package/query/fields.ts +216 -0
- package/query/invalidation.ts +48 -0
- package/query/kernel.ts +776 -0
- package/query/model.ts +79 -0
- package/repository.ts +374 -0
- package/runtime-profile.ts +34 -0
- package/schema/model.ts +92 -0
- package/schema/registry.ts +642 -0
- package/schema/standard.ts +226 -0
- package/server/context.ts +94 -0
- package/server/css-imports.ts +66 -0
- package/server/http.ts +333 -0
- package/server/page.ts +21 -0
- package/server/protocols.ts +12 -0
- package/server/representations.ts +66 -0
- package/server/router.ts +131 -0
- package/server/routes.ts +202 -0
- package/server/serve.ts +5 -0
- package/server/watch.ts +120 -0
- package/source/blocks.ts +71 -0
- package/source/format.ts +164 -0
- package/source/index.ts +67 -0
- package/source/interpolate.ts +63 -0
- package/source/markdown/blocks.ts +46 -0
- package/source/markdown/comments.ts +32 -0
- package/source/markdown/fences.ts +67 -0
- package/source/markdown/headings.ts +88 -0
- package/source/markdown/index.ts +34 -0
- package/source/markdown/inline-code.ts +6 -0
- package/source/markdown/links.ts +326 -0
- package/source/markdown/lists.ts +223 -0
- package/source/markdown/prose.ts +74 -0
- package/source/markdown/scan.ts +79 -0
- package/source/markdown/text.ts +82 -0
- package/source/meta.ts +87 -0
- package/source/source.ts +121 -0
- package/source/topmatter.ts +57 -0
- package/source/utils.ts +21 -0
- package/source-diagnostic.ts +63 -0
- package/text-file.ts +26 -0
- package/util.ts +35 -0
- package/view/components.ts +499 -0
- package/view/model.ts +91 -0
- package/view/projection.ts +162 -0
- package/view/session.ts +146 -0
- package/view/syntax.ts +372 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/** The Standard Schema interface. */
|
|
2
|
+
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
|
3
|
+
/** The Standard Schema properties. */
|
|
4
|
+
readonly "~standard": StandardSchemaV1.Props<Input, Output>
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function isStandardSchema(value: unknown): value is StandardSchemaV1 {
|
|
8
|
+
if (
|
|
9
|
+
value == null ||
|
|
10
|
+
(typeof value !== "object" && typeof value !== "function")
|
|
11
|
+
) {
|
|
12
|
+
return false
|
|
13
|
+
}
|
|
14
|
+
const standard = (value as { ["~standard"]?: unknown })["~standard"]
|
|
15
|
+
if (standard == null || typeof standard !== "object") return false
|
|
16
|
+
const props = standard as { version?: unknown; validate?: unknown }
|
|
17
|
+
return props.version === 1 && typeof props.validate === "function"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** The orthogonal Standard JSON Schema v1 discovery interface. */
|
|
21
|
+
export interface StandardJSONSchemaV1<Input = unknown, Output = Input> {
|
|
22
|
+
readonly "~standard": StandardJSONSchemaV1.Props<Input, Output>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export declare namespace StandardJSONSchemaV1 {
|
|
26
|
+
export interface Props<Input = unknown, Output = Input> {
|
|
27
|
+
readonly version: 1
|
|
28
|
+
readonly vendor: string
|
|
29
|
+
readonly types?: Types<Input, Output> | undefined
|
|
30
|
+
readonly jsonSchema: Converter
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface Converter {
|
|
34
|
+
readonly input: (options: Options) => Readonly<Record<string, unknown>>
|
|
35
|
+
readonly output: (options: Options) => Readonly<Record<string, unknown>>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type Target =
|
|
39
|
+
"draft-2020-12" | "draft-07" | "openapi-3.0" | ({} & string)
|
|
40
|
+
|
|
41
|
+
export interface Options {
|
|
42
|
+
readonly target: Target
|
|
43
|
+
readonly libraryOptions?: Record<string, unknown> | undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface Types<Input = unknown, Output = Input> {
|
|
47
|
+
readonly input: Input
|
|
48
|
+
readonly output: Output
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type InferInput<Schema extends StandardJSONSchemaV1> = NonNullable<
|
|
52
|
+
Schema["~standard"]["types"]
|
|
53
|
+
>["input"]
|
|
54
|
+
|
|
55
|
+
export type InferOutput<Schema extends StandardJSONSchemaV1> = NonNullable<
|
|
56
|
+
Schema["~standard"]["types"]
|
|
57
|
+
>["output"]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** PathMX data combines validation and transport-safe structural discovery. */
|
|
61
|
+
export type PathMXDataSchema<
|
|
62
|
+
Input = unknown,
|
|
63
|
+
Output = Input,
|
|
64
|
+
> = StandardSchemaV1<Input, Output> & StandardJSONSchemaV1<Input, Output>
|
|
65
|
+
|
|
66
|
+
export function isStandardJsonSchema(
|
|
67
|
+
value: unknown,
|
|
68
|
+
): value is StandardJSONSchemaV1 {
|
|
69
|
+
if (
|
|
70
|
+
value == null ||
|
|
71
|
+
(typeof value !== "object" && typeof value !== "function")
|
|
72
|
+
) {
|
|
73
|
+
return false
|
|
74
|
+
}
|
|
75
|
+
const jsonSchema = (value as Partial<StandardJSONSchemaV1>)["~standard"]
|
|
76
|
+
?.jsonSchema
|
|
77
|
+
return (
|
|
78
|
+
jsonSchema != null &&
|
|
79
|
+
typeof jsonSchema.input === "function" &&
|
|
80
|
+
typeof jsonSchema.output === "function"
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isJsonSchemaObject(
|
|
85
|
+
value: unknown,
|
|
86
|
+
): value is Readonly<Record<string, unknown>> {
|
|
87
|
+
return value != null && typeof value === "object" && !Array.isArray(value)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const requiredJsonSchemas = new WeakMap<
|
|
91
|
+
object,
|
|
92
|
+
Partial<Record<"input" | "output", Readonly<Record<string, unknown>>>>
|
|
93
|
+
>()
|
|
94
|
+
|
|
95
|
+
/** Convert an installed PathMX data schema or fail registration with context. */
|
|
96
|
+
export function requireStandardJsonSchema(
|
|
97
|
+
schema: unknown,
|
|
98
|
+
direction: "input" | "output",
|
|
99
|
+
subject: string,
|
|
100
|
+
) {
|
|
101
|
+
if (!isStandardSchema(schema) || !isStandardJsonSchema(schema)) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`${subject} must implement Standard Schema and Standard JSON Schema v1.`,
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
const cached = requiredJsonSchemas.get(schema)?.[direction]
|
|
107
|
+
if (cached) return cached
|
|
108
|
+
let converted: unknown
|
|
109
|
+
try {
|
|
110
|
+
converted = schema["~standard"].jsonSchema[direction]({
|
|
111
|
+
target: "draft-2020-12",
|
|
112
|
+
})
|
|
113
|
+
} catch (error) {
|
|
114
|
+
const detail = error instanceof Error ? error.message : String(error)
|
|
115
|
+
throw new Error(`${subject} ${direction} JSON Schema failed: ${detail}`)
|
|
116
|
+
}
|
|
117
|
+
if (!isJsonSchemaObject(converted)) {
|
|
118
|
+
throw new Error(`${subject} ${direction} JSON Schema must be an object.`)
|
|
119
|
+
}
|
|
120
|
+
const frozen = Object.freeze(converted)
|
|
121
|
+
const directions = requiredJsonSchemas.get(schema) ?? {}
|
|
122
|
+
directions[direction] = frozen
|
|
123
|
+
requiredJsonSchemas.set(schema, directions)
|
|
124
|
+
return frozen
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function standardJsonSchema(
|
|
128
|
+
schema: StandardSchemaV1 | undefined,
|
|
129
|
+
direction: "input" | "output",
|
|
130
|
+
) {
|
|
131
|
+
const converter = (schema as Partial<StandardJSONSchemaV1> | undefined)?.[
|
|
132
|
+
"~standard"
|
|
133
|
+
]?.jsonSchema?.[direction]
|
|
134
|
+
if (!converter) return
|
|
135
|
+
try {
|
|
136
|
+
return Object.freeze(converter({ target: "draft-2020-12" }))
|
|
137
|
+
} catch {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function formatSchemaIssues(
|
|
143
|
+
issues: ReadonlyArray<StandardSchemaV1.Issue>,
|
|
144
|
+
) {
|
|
145
|
+
return issues
|
|
146
|
+
.map((issue) => {
|
|
147
|
+
const path = issue.path
|
|
148
|
+
?.map((segment) =>
|
|
149
|
+
String(typeof segment === "object" ? segment.key : segment),
|
|
150
|
+
)
|
|
151
|
+
.join(".")
|
|
152
|
+
return path ? `${path}: ${issue.message}` : issue.message
|
|
153
|
+
})
|
|
154
|
+
.join("; ")
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export declare namespace StandardSchemaV1 {
|
|
158
|
+
/** The Standard Schema properties interface. */
|
|
159
|
+
export interface Props<Input = unknown, Output = Input> {
|
|
160
|
+
/** The version number of the standard. */
|
|
161
|
+
readonly version: 1
|
|
162
|
+
/** The vendor name of the schema library. */
|
|
163
|
+
readonly vendor: string
|
|
164
|
+
/** Validates unknown input values. */
|
|
165
|
+
readonly validate: (
|
|
166
|
+
value: unknown,
|
|
167
|
+
options?: StandardSchemaV1.Options | undefined,
|
|
168
|
+
) => Result<Output> | Promise<Result<Output>>
|
|
169
|
+
/** Inferred types associated with the schema. */
|
|
170
|
+
readonly types?: Types<Input, Output> | undefined
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The result interface of the validate function. */
|
|
174
|
+
export type Result<Output> = SuccessResult<Output> | FailureResult
|
|
175
|
+
|
|
176
|
+
/** The result interface if validation succeeds. */
|
|
177
|
+
export interface SuccessResult<Output> {
|
|
178
|
+
/** The typed output value. */
|
|
179
|
+
readonly value: Output
|
|
180
|
+
/** A falsy value for `issues` indicates success. */
|
|
181
|
+
readonly issues?: undefined
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface Options {
|
|
185
|
+
/** Explicit support for additional vendor-specific parameters, if needed. */
|
|
186
|
+
readonly libraryOptions?: Record<string, unknown> | undefined
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** The result interface if validation fails. */
|
|
190
|
+
export interface FailureResult {
|
|
191
|
+
/** The issues of failed validation. */
|
|
192
|
+
readonly issues: ReadonlyArray<Issue>
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** The issue interface of the failure output. */
|
|
196
|
+
export interface Issue {
|
|
197
|
+
/** The error message of the issue. */
|
|
198
|
+
readonly message: string
|
|
199
|
+
/** The path of the issue, if any. */
|
|
200
|
+
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** The path segment interface of the issue. */
|
|
204
|
+
export interface PathSegment {
|
|
205
|
+
/** The key representing a path segment. */
|
|
206
|
+
readonly key: PropertyKey
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** The Standard Schema types interface. */
|
|
210
|
+
export interface Types<Input = unknown, Output = Input> {
|
|
211
|
+
/** The input type of the schema. */
|
|
212
|
+
readonly input: Input
|
|
213
|
+
/** The output type of the schema. */
|
|
214
|
+
readonly output: Output
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Infers the input type of a Standard Schema. */
|
|
218
|
+
export type InferInput<Schema extends StandardSchemaV1> = NonNullable<
|
|
219
|
+
Schema["~standard"]["types"]
|
|
220
|
+
>["input"]
|
|
221
|
+
|
|
222
|
+
/** Infers the output type of a Standard Schema. */
|
|
223
|
+
export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<
|
|
224
|
+
Schema["~standard"]["types"]
|
|
225
|
+
>["output"]
|
|
226
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { AccessDeniedError } from "../authority/access.ts"
|
|
2
|
+
import { createTransaction } from "../actions/transaction.ts"
|
|
3
|
+
import type { Engine } from "../host/engine.ts"
|
|
4
|
+
import type { ServerContext, ServerRenderOptions } from "../plugins/types.ts"
|
|
5
|
+
import type { Source } from "../source/index.ts"
|
|
6
|
+
import { principalFor, type CredentialSession } from "../authority/model.ts"
|
|
7
|
+
import type { View } from "../view/model.ts"
|
|
8
|
+
import { renderPage } from "../host/render.ts"
|
|
9
|
+
import { pageNotFound, requestScheme } from "./page.ts"
|
|
10
|
+
|
|
11
|
+
/** @internal Bind one portable handler to its owning plugin and Engine. */
|
|
12
|
+
export function createServerContext(
|
|
13
|
+
app: Engine,
|
|
14
|
+
plugin: string,
|
|
15
|
+
path: string,
|
|
16
|
+
request: Request,
|
|
17
|
+
url: URL,
|
|
18
|
+
): ServerContext {
|
|
19
|
+
let resolvedSession: Promise<CredentialSession> | undefined
|
|
20
|
+
let resolvedView: Promise<View> | undefined
|
|
21
|
+
const session = () => (resolvedSession ??= app.resolveCredentials(request))
|
|
22
|
+
const view = () =>
|
|
23
|
+
(resolvedView ??= session().then((value) => app.view(value)))
|
|
24
|
+
const appContext = app.appContext(plugin)
|
|
25
|
+
const resolveSource = (input: Source | string) =>
|
|
26
|
+
typeof input === "string" ? appContext.sources.get(input) : input
|
|
27
|
+
|
|
28
|
+
return Object.freeze({
|
|
29
|
+
request,
|
|
30
|
+
url,
|
|
31
|
+
path,
|
|
32
|
+
plugin,
|
|
33
|
+
log: appContext.log,
|
|
34
|
+
runtime: appContext.runtime,
|
|
35
|
+
database: appContext.database,
|
|
36
|
+
graph: appContext.graph,
|
|
37
|
+
source(idOrPath) {
|
|
38
|
+
return appContext.sources.get(idOrPath)
|
|
39
|
+
},
|
|
40
|
+
sources() {
|
|
41
|
+
return appContext.sources.all()
|
|
42
|
+
},
|
|
43
|
+
resolve(pathname) {
|
|
44
|
+
return appContext.sources.resolve(pathname)
|
|
45
|
+
},
|
|
46
|
+
isPublic(target) {
|
|
47
|
+
return appContext.access.read({ type: "anonymous" }, target) === "public"
|
|
48
|
+
},
|
|
49
|
+
session,
|
|
50
|
+
view,
|
|
51
|
+
async readAccess(target) {
|
|
52
|
+
const credentials = await session()
|
|
53
|
+
return appContext.access.read(principalFor(credentials), target)
|
|
54
|
+
},
|
|
55
|
+
onSync(listener) {
|
|
56
|
+
return app.onSync(listener)
|
|
57
|
+
},
|
|
58
|
+
async render(input, options: ServerRenderOptions = {}) {
|
|
59
|
+
const source = resolveSource(input)
|
|
60
|
+
if (!source) return pageNotFound()
|
|
61
|
+
const credentials = await session()
|
|
62
|
+
return renderPage(app, credentials, principalFor(credentials), source, {
|
|
63
|
+
...options,
|
|
64
|
+
scheme: requestScheme(request),
|
|
65
|
+
})
|
|
66
|
+
},
|
|
67
|
+
async mutate(run) {
|
|
68
|
+
const credentials = await session()
|
|
69
|
+
if (credentials.type !== "authenticated") {
|
|
70
|
+
throw new AccessDeniedError("authentication-required")
|
|
71
|
+
}
|
|
72
|
+
const result = await app.commitPrepared(async () => {
|
|
73
|
+
const transaction = createTransaction(app, credentials.actor)
|
|
74
|
+
await run(transaction)
|
|
75
|
+
return transaction.changeSet()
|
|
76
|
+
})
|
|
77
|
+
if (result.applied.length) {
|
|
78
|
+
app.log.info("server.mutation", {
|
|
79
|
+
plugin,
|
|
80
|
+
method: request.method,
|
|
81
|
+
path: url.pathname,
|
|
82
|
+
changes: result.applied.length,
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
return result
|
|
86
|
+
},
|
|
87
|
+
mutateAsAgent(admission, run) {
|
|
88
|
+
return app.runAgentJob(admission, run)
|
|
89
|
+
},
|
|
90
|
+
handleCredentials() {
|
|
91
|
+
return app.credentialRequest(request)
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { posix } from "node:path"
|
|
2
|
+
|
|
3
|
+
const CSS_IMPORT =
|
|
4
|
+
/@import\s+(?:url\(\s*(?:"([^"]+)"|'([^']+)'|([^'"\s)]+))\s*\)|"([^"]+)"|'([^']+)')/gi
|
|
5
|
+
const EXTERNAL = /^(?:[a-z][a-z\d+.-]*:|\/\/|#)/i
|
|
6
|
+
|
|
7
|
+
function importedPath(importer: string, specifier: string) {
|
|
8
|
+
const raw = specifier.split(/[?#]/, 1)[0]
|
|
9
|
+
if (!raw || EXTERNAL.test(raw)) return
|
|
10
|
+
const path = posix.resolve(posix.dirname(importer), raw)
|
|
11
|
+
return path.endsWith(".css") ? path : undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function cssImports(importer: string, css: string) {
|
|
15
|
+
const imports = new Set<string>()
|
|
16
|
+
for (const match of css.matchAll(CSS_IMPORT)) {
|
|
17
|
+
const specifier = match.slice(1).find((value) => value !== undefined)
|
|
18
|
+
if (!specifier) continue
|
|
19
|
+
const path = importedPath(importer, specifier)
|
|
20
|
+
if (path) imports.add(path)
|
|
21
|
+
}
|
|
22
|
+
return imports
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Internal reverse index for native authored CSS imports. */
|
|
26
|
+
export class CssImportIndex {
|
|
27
|
+
private dependencies = new Map<string, Set<string>>()
|
|
28
|
+
private dependents = new Map<string, Set<string>>()
|
|
29
|
+
|
|
30
|
+
replace(importer: string, css?: string) {
|
|
31
|
+
for (const dependency of this.dependencies.get(importer) ?? []) {
|
|
32
|
+
const importers = this.dependents.get(dependency)
|
|
33
|
+
importers?.delete(importer)
|
|
34
|
+
if (!importers?.size) this.dependents.delete(dependency)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (css === undefined) {
|
|
38
|
+
this.dependencies.delete(importer)
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const imports = cssImports(importer, css)
|
|
43
|
+
this.dependencies.set(importer, imports)
|
|
44
|
+
for (const dependency of imports) {
|
|
45
|
+
let importers = this.dependents.get(dependency)
|
|
46
|
+
if (!importers) {
|
|
47
|
+
importers = new Set()
|
|
48
|
+
this.dependents.set(dependency, importers)
|
|
49
|
+
}
|
|
50
|
+
importers.add(importer)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
affected(path: string) {
|
|
55
|
+
const affected = new Set([path])
|
|
56
|
+
const queue = [path]
|
|
57
|
+
for (const dependency of queue) {
|
|
58
|
+
for (const importer of this.dependents.get(dependency) ?? []) {
|
|
59
|
+
if (affected.has(importer)) continue
|
|
60
|
+
affected.add(importer)
|
|
61
|
+
queue.push(importer)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return affected
|
|
65
|
+
}
|
|
66
|
+
}
|