@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
package/host/create.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Engine } from "./engine.ts"
|
|
2
|
+
import { ActorDirectory, ActorDirectoryPlugin } from "../authority/actors.ts"
|
|
3
|
+
import { SourceAccessIndex, SourceAccessPlugin } from "../authority/access.ts"
|
|
4
|
+
import type { PathMXEnvironment } from "../environment.ts"
|
|
5
|
+
import type { Log } from "../log.ts"
|
|
6
|
+
import { MetaPlugin } from "../plugins/meta.ts"
|
|
7
|
+
import { SchemaPlugin } from "../plugins/schema.ts"
|
|
8
|
+
import {
|
|
9
|
+
expandPluginContributions,
|
|
10
|
+
type PluginContribution,
|
|
11
|
+
} from "../plugins/preset.ts"
|
|
12
|
+
import { Repository } from "../repository.ts"
|
|
13
|
+
import type { RuntimeProfileOptions } from "../runtime-profile.ts"
|
|
14
|
+
import type { SourceDiagnosticMode } from "../source-diagnostic.ts"
|
|
15
|
+
import { projectDirectory } from "../project.ts"
|
|
16
|
+
|
|
17
|
+
export type CreateAppOptions = {
|
|
18
|
+
root: string
|
|
19
|
+
environment: PathMXEnvironment
|
|
20
|
+
actionsRoot?: string
|
|
21
|
+
stateRoot?: string
|
|
22
|
+
/** Compile every page at startup instead of caching pages on first request. */
|
|
23
|
+
buildCache?: boolean
|
|
24
|
+
plugins?: readonly PluginContribution[]
|
|
25
|
+
runtime?: RuntimeProfileOptions
|
|
26
|
+
diagnostics?: SourceDiagnosticMode
|
|
27
|
+
log?: Log
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function createApp(options: CreateAppOptions) {
|
|
31
|
+
const repo = new Repository(options.root, options.environment)
|
|
32
|
+
const actors = new ActorDirectory()
|
|
33
|
+
const access = new SourceAccessIndex(repo, actors)
|
|
34
|
+
const app = new Engine(
|
|
35
|
+
repo,
|
|
36
|
+
options.environment,
|
|
37
|
+
actors,
|
|
38
|
+
actors.profiles,
|
|
39
|
+
access,
|
|
40
|
+
options.log,
|
|
41
|
+
options.runtime,
|
|
42
|
+
options.diagnostics,
|
|
43
|
+
options.stateRoot ?? projectDirectory(options.root, ".pathmx/state"),
|
|
44
|
+
)
|
|
45
|
+
if (options.actionsRoot) app.actionsRoot = options.actionsRoot
|
|
46
|
+
try {
|
|
47
|
+
app.registerPlugin(ActorDirectoryPlugin(actors))
|
|
48
|
+
app.registerPlugin(SourceAccessPlugin(access))
|
|
49
|
+
app.registerPlugin(MetaPlugin)
|
|
50
|
+
app.registerPlugin(SchemaPlugin)
|
|
51
|
+
for (const plugin of expandPluginContributions(options.plugins ?? [])) {
|
|
52
|
+
app.registerPlugin(plugin)
|
|
53
|
+
}
|
|
54
|
+
app.freezePlugins()
|
|
55
|
+
await app.setupPlugins()
|
|
56
|
+
await app.perf.measureAsync("repo.load", () => repo.load())
|
|
57
|
+
await app.indexAllNodes()
|
|
58
|
+
if (options.diagnostics !== "collect") access.validate()
|
|
59
|
+
await app.readyCredentials()
|
|
60
|
+
if (options.buildCache) await app.buildCache()
|
|
61
|
+
return app
|
|
62
|
+
} catch (error) {
|
|
63
|
+
await app.close().catch(() => undefined)
|
|
64
|
+
throw error
|
|
65
|
+
}
|
|
66
|
+
}
|
package/host/engine.ts
ADDED
|
@@ -0,0 +1,630 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Actor,
|
|
3
|
+
ActorProfiles,
|
|
4
|
+
CredentialSession,
|
|
5
|
+
KnownActorIndex,
|
|
6
|
+
Principal,
|
|
7
|
+
} from "../authority/model"
|
|
8
|
+
import type {
|
|
9
|
+
AssetContext,
|
|
10
|
+
CompiledPage,
|
|
11
|
+
CompileContext,
|
|
12
|
+
DocumentAsset,
|
|
13
|
+
PageContext,
|
|
14
|
+
Plugin,
|
|
15
|
+
PluginFactory,
|
|
16
|
+
ViewContext,
|
|
17
|
+
} from "../plugins/types"
|
|
18
|
+
import {
|
|
19
|
+
type AccessDecision,
|
|
20
|
+
type ProposedSourceChange,
|
|
21
|
+
type ReadAccess,
|
|
22
|
+
type SourceAccessInspection,
|
|
23
|
+
type SourceCapability,
|
|
24
|
+
type SourcePermissionRecord,
|
|
25
|
+
type SourceAccessPolicy,
|
|
26
|
+
} from "../authority/access"
|
|
27
|
+
import {
|
|
28
|
+
indexSourceDocument,
|
|
29
|
+
type Source,
|
|
30
|
+
type SourceBlock,
|
|
31
|
+
} from "../source/index"
|
|
32
|
+
import { type Dependency, type DependencyContext } from "../dependencies"
|
|
33
|
+
import type { RouteInvalidation } from "../invalidation"
|
|
34
|
+
import { Graph } from "../graph"
|
|
35
|
+
import {
|
|
36
|
+
type CommitResult,
|
|
37
|
+
type SourceChangeSet,
|
|
38
|
+
Repository,
|
|
39
|
+
} from "../repository"
|
|
40
|
+
import type { BundledAsset, PathMXEnvironment } from "../environment"
|
|
41
|
+
import { Log } from "../log"
|
|
42
|
+
import { Perf } from "../perf"
|
|
43
|
+
import { defaultActionsRoot } from "../actions/actions"
|
|
44
|
+
import { indexRouteConflicts, publicPath, resolveRoute } from "../server/router"
|
|
45
|
+
import { ActorSourceError } from "../authority/actors.ts"
|
|
46
|
+
import { SourceAccessError } from "../authority/access.ts"
|
|
47
|
+
import {
|
|
48
|
+
runAgentJobWithIndex,
|
|
49
|
+
type AgentAdmission,
|
|
50
|
+
type AgentJob,
|
|
51
|
+
} from "../authority/agents.ts"
|
|
52
|
+
import {
|
|
53
|
+
createAssetBag,
|
|
54
|
+
documentAssetRank,
|
|
55
|
+
renderDocumentAssets,
|
|
56
|
+
} from "../document.ts"
|
|
57
|
+
import { GeneratedAssets } from "../assets.ts"
|
|
58
|
+
import { PluginHost } from "./plugin-host.ts"
|
|
59
|
+
import { PageCompiler } from "./compiler.ts"
|
|
60
|
+
import { SourceSynchronizer } from "./source-sync.ts"
|
|
61
|
+
import { PluginDatabases } from "../database.ts"
|
|
62
|
+
import { projectDirectory } from "../project.ts"
|
|
63
|
+
import { browserBundleAssets } from "../browser-bundle.ts"
|
|
64
|
+
import {
|
|
65
|
+
createRuntimeProfile,
|
|
66
|
+
type RuntimeProfile,
|
|
67
|
+
type RuntimeProfileOptions,
|
|
68
|
+
} from "../runtime-profile.ts"
|
|
69
|
+
import { createView } from "../view/session.ts"
|
|
70
|
+
import {
|
|
71
|
+
SourceDiagnostics,
|
|
72
|
+
type SourceDiagnostic,
|
|
73
|
+
type SourceDiagnosticMode,
|
|
74
|
+
} from "../source-diagnostic.ts"
|
|
75
|
+
import type { ApplicationSchema } from "../schema/model.ts"
|
|
76
|
+
import { Query } from "../query/kernel.ts"
|
|
77
|
+
import type { QueryContribution } from "../query/model.ts"
|
|
78
|
+
|
|
79
|
+
export { PluginRegistrationError } from "./plugin-host.ts"
|
|
80
|
+
|
|
81
|
+
export class Engine {
|
|
82
|
+
environment: PathMXEnvironment
|
|
83
|
+
readonly runtime: RuntimeProfile
|
|
84
|
+
repo: Repository
|
|
85
|
+
graph: Graph
|
|
86
|
+
readonly profiles: ActorProfiles
|
|
87
|
+
actionsRoot: string
|
|
88
|
+
perf = new Perf()
|
|
89
|
+
log: Log
|
|
90
|
+
private bundles = new Map<string, Promise<BundledAsset[]>>()
|
|
91
|
+
private assetRevisions = new Map<string, number>()
|
|
92
|
+
private generatedAssets: GeneratedAssets
|
|
93
|
+
private actorIndex: KnownActorIndex
|
|
94
|
+
private accessPolicy: SourceAccessPolicy
|
|
95
|
+
private authorityErrors = new Map<string, string>()
|
|
96
|
+
private sourceDiagnosticStore: SourceDiagnostics
|
|
97
|
+
private pluginHost: PluginHost
|
|
98
|
+
readonly queries: Query
|
|
99
|
+
private compiler: PageCompiler
|
|
100
|
+
private sourceSync: SourceSynchronizer
|
|
101
|
+
|
|
102
|
+
constructor(
|
|
103
|
+
repo: Repository,
|
|
104
|
+
environment: PathMXEnvironment,
|
|
105
|
+
actors: KnownActorIndex,
|
|
106
|
+
profiles: ActorProfiles,
|
|
107
|
+
access: SourceAccessPolicy,
|
|
108
|
+
log = new Log(),
|
|
109
|
+
runtime?: RuntimeProfileOptions,
|
|
110
|
+
diagnostics: SourceDiagnosticMode = "throw",
|
|
111
|
+
stateRoot = projectDirectory(repo.rootPath, ".pathmx/state"),
|
|
112
|
+
) {
|
|
113
|
+
this.repo = repo
|
|
114
|
+
this.environment = environment
|
|
115
|
+
this.runtime = createRuntimeProfile(runtime)
|
|
116
|
+
this.generatedAssets =
|
|
117
|
+
this.runtime.target === "static"
|
|
118
|
+
? new GeneratedAssets(Number.POSITIVE_INFINITY)
|
|
119
|
+
: new GeneratedAssets()
|
|
120
|
+
this.graph = new Graph(repo)
|
|
121
|
+
this.actorIndex = actors
|
|
122
|
+
this.profiles = profiles
|
|
123
|
+
this.accessPolicy = access
|
|
124
|
+
this.log = log
|
|
125
|
+
this.pluginHost = new PluginHost(
|
|
126
|
+
new PluginDatabases(environment.database, stateRoot),
|
|
127
|
+
)
|
|
128
|
+
this.queries = new Query(this)
|
|
129
|
+
this.compiler = new PageCompiler(this, this.pluginHost)
|
|
130
|
+
this.sourceSync = new SourceSynchronizer(
|
|
131
|
+
this,
|
|
132
|
+
this.pluginHost,
|
|
133
|
+
this.compiler,
|
|
134
|
+
)
|
|
135
|
+
this.sourceDiagnosticStore = new SourceDiagnostics(diagnostics, log)
|
|
136
|
+
this.actionsRoot = defaultActionsRoot(repo.rootPath)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
registerPlugin(plugin: PluginFactory | Plugin) {
|
|
140
|
+
this.pluginHost.register(plugin)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
freezePlugins() {
|
|
144
|
+
this.pluginHost.freeze()
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
setupPlugins() {
|
|
148
|
+
return this.pluginHost.setup(this)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
appContext(plugin: string) {
|
|
152
|
+
return this.pluginHost.appContext(plugin, this)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
get plugins() {
|
|
156
|
+
return this.pluginHost.plugins
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
get schema(): ApplicationSchema {
|
|
160
|
+
return this.pluginHost.schema
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
registeredQuery(reference: string | QueryContribution) {
|
|
164
|
+
return this.pluginHost.query(reference)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
actor(id: string) {
|
|
168
|
+
return this.actorIndex.findActorById(id)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get pageCache() {
|
|
172
|
+
return this.compiler.pageCache
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
dependentsOf(dependency: string | Dependency) {
|
|
176
|
+
return this.compiler.dependentsOf(dependency)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
dependenciesOf(path: string) {
|
|
180
|
+
return this.compiler.dependenciesOf(path)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Bundle a browser entrypoint once per Engine; concurrent calls share one build. */
|
|
184
|
+
bundle(entrypoint: string | URL) {
|
|
185
|
+
const key = String(entrypoint)
|
|
186
|
+
let pending = this.bundles.get(key)
|
|
187
|
+
if (!pending) {
|
|
188
|
+
pending = this.perf.measureAsync(
|
|
189
|
+
"bundle",
|
|
190
|
+
() => this.environment.bundle(entrypoint),
|
|
191
|
+
key,
|
|
192
|
+
)
|
|
193
|
+
this.bundles.set(key, pending)
|
|
194
|
+
}
|
|
195
|
+
return pending
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** @internal Create the asset collector shared by host compilation paths. */
|
|
199
|
+
documentAssetBag(assets: DocumentAsset[] = []) {
|
|
200
|
+
const bag = createAssetBag(assets)
|
|
201
|
+
const addBrowserBundle: AssetContext["addBrowserBundle"] = async (
|
|
202
|
+
entrypoint,
|
|
203
|
+
name,
|
|
204
|
+
) => {
|
|
205
|
+
bag.addAsset(...(await browserBundleAssets(this, entrypoint, name)))
|
|
206
|
+
}
|
|
207
|
+
return { ...bag, addBrowserBundle }
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async documentAssets(page?: CompiledPage) {
|
|
211
|
+
const { assets, addAsset, addBrowserBundle } = this.documentAssetBag()
|
|
212
|
+
const ctx = { page, addAsset, addBrowserBundle }
|
|
213
|
+
await this.pluginHost.documentAssets(ctx, this)
|
|
214
|
+
addAsset(...(page?.assets ?? []))
|
|
215
|
+
return assets
|
|
216
|
+
.map((asset) => this.resolveDocumentAsset(asset))
|
|
217
|
+
.sort((a, b) => documentAssetRank(a) - documentAssetRank(b))
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
servedAsset(pathname: string) {
|
|
221
|
+
return this.generatedAssets.get(pathname)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** @internal Enumerate retained generated artifacts during static publication. */
|
|
225
|
+
generatedAssetEntries() {
|
|
226
|
+
return this.generatedAssets.entries()
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** Publish an immutable, content-hashed binary asset under `/.pmx/`. */
|
|
230
|
+
async publishAsset(name: string, blob: Blob, contentType = blob.type) {
|
|
231
|
+
return this.generatedAssets.publishBlob(name, blob, contentType)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
onSync(listener: (event: RouteInvalidation) => void) {
|
|
235
|
+
return this.sourceSync.onSync(listener)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private resolveDocumentAsset(asset: DocumentAsset): DocumentAsset {
|
|
239
|
+
if (asset.type === "stylesheet" && "name" in asset) {
|
|
240
|
+
const href = this.storeNamed(
|
|
241
|
+
asset.name,
|
|
242
|
+
asset.text,
|
|
243
|
+
"text/css; charset=utf-8",
|
|
244
|
+
)
|
|
245
|
+
return {
|
|
246
|
+
type: "stylesheet",
|
|
247
|
+
href,
|
|
248
|
+
...(asset.media ? { media: asset.media } : {}),
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (asset.type === "script" && "name" in asset) {
|
|
252
|
+
const href = this.storeNamed(
|
|
253
|
+
asset.name,
|
|
254
|
+
asset.text,
|
|
255
|
+
"text/javascript; charset=utf-8",
|
|
256
|
+
)
|
|
257
|
+
return { type: "script", href, module: asset.module }
|
|
258
|
+
}
|
|
259
|
+
if (asset.type === "script" && "source" in asset) {
|
|
260
|
+
const revision = this.assetRevisions.get(asset.source)
|
|
261
|
+
const href = revision ? `${asset.source}?pmx=${revision}` : asset.source
|
|
262
|
+
return { type: "script", href, module: asset.module }
|
|
263
|
+
}
|
|
264
|
+
return asset
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
bumpAssetRevisions(paths: Iterable<string>) {
|
|
268
|
+
for (const path of paths) {
|
|
269
|
+
const graphPath = this.repo.graphPath(path)
|
|
270
|
+
this.assetRevisions.set(
|
|
271
|
+
graphPath,
|
|
272
|
+
(this.assetRevisions.get(graphPath) ?? 0) + 1,
|
|
273
|
+
)
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private storeNamed(name: string, text: string, contentType: string) {
|
|
278
|
+
return this.generatedAssets.publishText(name, text, contentType)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async documentHead(page?: CompiledPage) {
|
|
282
|
+
return renderDocumentAssets(await this.documentAssets(page))
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
resolve(pathname: string) {
|
|
286
|
+
const route = resolveRoute(this.repo, pathname)
|
|
287
|
+
return route?.type === "page" ? route.source : undefined
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
resolveCredentials(request: Request) {
|
|
291
|
+
return this.pluginHost.resolveCredentials(request, this.actorIndex, this)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Create a lightweight live View from a host-admitted credential session. */
|
|
295
|
+
view(session: CredentialSession) {
|
|
296
|
+
return createView(this, session)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
credentialRequest(request: Request) {
|
|
300
|
+
return this.pluginHost.credentialRequest(request, this.actorIndex)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
readyCredentials() {
|
|
304
|
+
return this.pluginHost.readyCredentials(this.actorIndex)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
serverRoute(request: Request, url = new URL(request.url), publicUrl = url) {
|
|
308
|
+
return this.pluginHost.serverRoute(request, url, this, publicUrl)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** @internal Test whether an optional behavior-owning Plugin is installed. */
|
|
312
|
+
hasPlugin(id: string) {
|
|
313
|
+
return this.pluginHost.has(id)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/** @internal Resolve a namespaced Action contributed by a Plugin. */
|
|
317
|
+
pluginAction(name: string) {
|
|
318
|
+
return this.pluginHost.action(name)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/** @internal Project contextual Actions registered by Plugins. */
|
|
322
|
+
actionAffordances(principal: Principal, target: Source) {
|
|
323
|
+
return this.pluginHost.actionAffordances(principal, target, this)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
closeCredentials() {
|
|
327
|
+
return this.pluginHost.closeCredentials()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
close() {
|
|
331
|
+
return this.pluginHost.close(this)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
homeActorId(path: string) {
|
|
335
|
+
return this.actorIndex.findHomeOwner(path)?.id
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/** Admit a configured Agent without exposing the private Actor directory. */
|
|
339
|
+
runAgentJob(admission: AgentAdmission, job: AgentJob) {
|
|
340
|
+
return runAgentJobWithIndex(this, this.actorIndex, admission, job)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
readAccess(principal: Principal, target: string | Source): ReadAccess {
|
|
344
|
+
return this.accessPolicy.readAccess(principal, target)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
inspectSourceAccess(
|
|
348
|
+
principal: Principal,
|
|
349
|
+
target: string | Source,
|
|
350
|
+
): SourceAccessInspection {
|
|
351
|
+
return this.accessPolicy.inspect(principal, target)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/** Privileged repository inspection for administrative tools such as the CLI. */
|
|
355
|
+
inspectSourcePermissions(target: Source): readonly SourcePermissionRecord[] {
|
|
356
|
+
return [
|
|
357
|
+
...this.actorIndex.all().map((actor) => ({
|
|
358
|
+
sourceId: target.id,
|
|
359
|
+
actorId: actor.id,
|
|
360
|
+
...this.accessPolicy.inspect({ type: "actor", actor }, target),
|
|
361
|
+
})),
|
|
362
|
+
{
|
|
363
|
+
sourceId: target.id,
|
|
364
|
+
actorId: null,
|
|
365
|
+
...this.accessPolicy.inspect({ type: "anonymous" }, target),
|
|
366
|
+
},
|
|
367
|
+
]
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Privileged repository inspection; unlike a credential session, this does not impersonate. */
|
|
371
|
+
inspectActorPermissions(
|
|
372
|
+
actorId: string,
|
|
373
|
+
): readonly SourcePermissionRecord[] | undefined {
|
|
374
|
+
const actor = this.actorIndex.findActorById(actorId)
|
|
375
|
+
if (!actor) return
|
|
376
|
+
const principal = { type: "actor" as const, actor }
|
|
377
|
+
return this.repo
|
|
378
|
+
.all()
|
|
379
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
380
|
+
.map((source) => ({
|
|
381
|
+
sourceId: source.id,
|
|
382
|
+
actorId,
|
|
383
|
+
...this.accessPolicy.inspect(principal, source),
|
|
384
|
+
}))
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
hasSourceCapability(
|
|
388
|
+
principal: Principal,
|
|
389
|
+
target: string | Source,
|
|
390
|
+
capability: SourceCapability,
|
|
391
|
+
) {
|
|
392
|
+
return this.accessPolicy.hasCapability(principal, target, capability)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
authorizeChange(actor: Actor, change: ProposedSourceChange): AccessDecision {
|
|
396
|
+
return this.accessPolicy.authorizeChange(actor, change)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
canEmbed(container: string, dependency: string) {
|
|
400
|
+
return this.accessPolicy.canEmbed(container, dependency)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
forgetNode(nodePath: string) {
|
|
404
|
+
this.compiler.unindexSource(nodePath)
|
|
405
|
+
this.sourceDiagnosticStore.delete(nodePath)
|
|
406
|
+
this.pluginHost.unindex(nodePath, this)
|
|
407
|
+
this.queries.unindexSource(nodePath)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** @internal Clear a degraded authority record after its Source is removed. */
|
|
411
|
+
clearAuthorityError(sourceId: string) {
|
|
412
|
+
this.authorityErrors.delete(sourceId)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/** @internal Expected authoring failures collected while indexing. */
|
|
416
|
+
sourceDiagnostics(): readonly SourceDiagnostic[] {
|
|
417
|
+
return this.sourceDiagnosticStore.all()
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
indexSource(source: Source, tolerateAuthorityErrors = false) {
|
|
421
|
+
return this.perf.measure(
|
|
422
|
+
"index.source",
|
|
423
|
+
() => {
|
|
424
|
+
this.forgetNode(source.path)
|
|
425
|
+
this.compiler.indexSource(source)
|
|
426
|
+
this.authorityErrors.delete(source.id)
|
|
427
|
+
const capture = (error: unknown) => {
|
|
428
|
+
if (this.sourceDiagnosticStore.capture(error)) return true
|
|
429
|
+
if (
|
|
430
|
+
!tolerateAuthorityErrors ||
|
|
431
|
+
!(
|
|
432
|
+
error instanceof ActorSourceError ||
|
|
433
|
+
error instanceof SourceAccessError
|
|
434
|
+
)
|
|
435
|
+
) {
|
|
436
|
+
return false
|
|
437
|
+
}
|
|
438
|
+
this.authorityErrors.set(source.id, error.message)
|
|
439
|
+
this.log.error("authority.degraded", {
|
|
440
|
+
source: source.id,
|
|
441
|
+
message: error.message,
|
|
442
|
+
})
|
|
443
|
+
return true
|
|
444
|
+
}
|
|
445
|
+
try {
|
|
446
|
+
indexSourceDocument(source, this.environment.yaml)
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (!capture(error)) throw error
|
|
449
|
+
return
|
|
450
|
+
}
|
|
451
|
+
this.pluginHost.index(source, this, capture)
|
|
452
|
+
this.queries.indexSource(source)
|
|
453
|
+
},
|
|
454
|
+
source.path,
|
|
455
|
+
)
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
async indexAllNodes() {
|
|
459
|
+
this.log.info("index", { sources: this.repo.all().length })
|
|
460
|
+
for (const source of this.repo.all()) this.indexSource(source)
|
|
461
|
+
for (const conflict of indexRouteConflicts(this.repo)) {
|
|
462
|
+
this.log.warn("route.index.conflict", {
|
|
463
|
+
dir: conflict.dir,
|
|
464
|
+
winner: conflict.winner.path,
|
|
465
|
+
shadowed: conflict.shadowed.map((source) => source.path),
|
|
466
|
+
rule: "first index source by id, codepoint order",
|
|
467
|
+
})
|
|
468
|
+
}
|
|
469
|
+
await this.perf.measureAsync("graph.rebuild", () => this.graph.rebuild())
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
authorityReadiness() {
|
|
473
|
+
const errors = [
|
|
474
|
+
...[...this.authorityErrors].map(([sourceId, message]) => ({
|
|
475
|
+
sourceId,
|
|
476
|
+
message,
|
|
477
|
+
})),
|
|
478
|
+
...(this.accessPolicy.diagnostics?.() ?? []),
|
|
479
|
+
]
|
|
480
|
+
.filter(
|
|
481
|
+
(error, index, all) =>
|
|
482
|
+
all.findIndex(
|
|
483
|
+
(candidate) =>
|
|
484
|
+
candidate.sourceId === error.sourceId &&
|
|
485
|
+
candidate.message === error.message,
|
|
486
|
+
) === index,
|
|
487
|
+
)
|
|
488
|
+
.sort(
|
|
489
|
+
(a, b) =>
|
|
490
|
+
a.sourceId.localeCompare(b.sourceId) ||
|
|
491
|
+
a.message.localeCompare(b.message),
|
|
492
|
+
)
|
|
493
|
+
return errors.length
|
|
494
|
+
? {
|
|
495
|
+
type: "degraded" as const,
|
|
496
|
+
errors,
|
|
497
|
+
}
|
|
498
|
+
: { type: "ready" as const, errors: [] }
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
isAuthorized(source: Source, actor: Actor) {
|
|
502
|
+
return this.readAccess({ type: "actor", actor }, source) !== "denied"
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
isAuthorizedPath(path: string, actor: Actor) {
|
|
506
|
+
const source = this.repo.atPath(path) ?? this.repo.get(path)
|
|
507
|
+
if (source) return this.isAuthorized(source, actor)
|
|
508
|
+
return this.readAccess({ type: "actor", actor }, path) !== "denied"
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
compilePage(source: Source) {
|
|
512
|
+
return this.compiler.compilePage(source)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** @internal Release state retained only for live recompilation. */
|
|
516
|
+
forgetCompiledPage(path: string) {
|
|
517
|
+
this.compiler.forgetCompiled(path)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
ensurePage(source: Source) {
|
|
521
|
+
return this.compiler.ensurePage(source)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
compileDocument(
|
|
525
|
+
source: Source,
|
|
526
|
+
dependencies: DependencyContext,
|
|
527
|
+
excludedPluginId: string,
|
|
528
|
+
) {
|
|
529
|
+
return this.compiler.compileDocument(source, dependencies, excludedPluginId)
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
compileBlock(
|
|
533
|
+
source: Source,
|
|
534
|
+
block: SourceBlock,
|
|
535
|
+
dependencies?: DependencyContext,
|
|
536
|
+
options?: { id?: string },
|
|
537
|
+
scope?: CompileContext["scope"],
|
|
538
|
+
) {
|
|
539
|
+
return this.compiler.compileBlock(
|
|
540
|
+
source,
|
|
541
|
+
block,
|
|
542
|
+
dependencies,
|
|
543
|
+
options,
|
|
544
|
+
scope,
|
|
545
|
+
)
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
isPage(source: Source) {
|
|
549
|
+
return this.compiler.isPage(source)
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
buildCache() {
|
|
553
|
+
return this.compiler.buildCache()
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
dropNode(nodePath: string) {
|
|
557
|
+
return this.sourceSync.dropNode(nodePath)
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
commit(changeSet: SourceChangeSet) {
|
|
561
|
+
return this.sourceSync.commit(changeSet)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
commitPrepared(prepare: () => Promise<SourceChangeSet>) {
|
|
565
|
+
return this.sourceSync.commitPrepared(prepare)
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
refresh(
|
|
569
|
+
result: CommitResult,
|
|
570
|
+
before = new Map<string, Source | undefined>(),
|
|
571
|
+
) {
|
|
572
|
+
return this.sourceSync.refresh(result, before)
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
syncSource(path: string) {
|
|
576
|
+
return this.sourceSync.syncSource(path)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
invalidateAsset(path: string) {
|
|
580
|
+
return this.sourceSync.invalidateAsset(path)
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
invalidateAssets(paths: Iterable<string>) {
|
|
584
|
+
return this.sourceSync.invalidateAssets(paths)
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
syncFromStorage(relPath: string) {
|
|
588
|
+
return this.sourceSync.syncFromStorage(relPath)
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
rewrite(
|
|
592
|
+
session: CredentialSession,
|
|
593
|
+
html: string,
|
|
594
|
+
source: Source,
|
|
595
|
+
init?: ResponseInit,
|
|
596
|
+
) {
|
|
597
|
+
return this.perf.measureAsync(
|
|
598
|
+
"rewrite",
|
|
599
|
+
() => {
|
|
600
|
+
const view = this.view(session)
|
|
601
|
+
return this.rewriteWithPlugins(
|
|
602
|
+
html,
|
|
603
|
+
{
|
|
604
|
+
view,
|
|
605
|
+
session,
|
|
606
|
+
principal: view.principal,
|
|
607
|
+
source,
|
|
608
|
+
route: publicPath(source),
|
|
609
|
+
},
|
|
610
|
+
init,
|
|
611
|
+
)
|
|
612
|
+
},
|
|
613
|
+
source.path,
|
|
614
|
+
)
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
private async rewriteWithPlugins(
|
|
618
|
+
html: string,
|
|
619
|
+
ctx: ViewContext,
|
|
620
|
+
init?: ResponseInit,
|
|
621
|
+
) {
|
|
622
|
+
return this.pluginHost.renderView(
|
|
623
|
+
html,
|
|
624
|
+
this.compiler.planFor(ctx.source.path),
|
|
625
|
+
ctx,
|
|
626
|
+
this,
|
|
627
|
+
init,
|
|
628
|
+
)
|
|
629
|
+
}
|
|
630
|
+
}
|