@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/database.ts
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import type { PathMXDatabaseEnvironment } from "./environment.ts"
|
|
2
|
+
|
|
3
|
+
export type SqlValue = string | number | bigint | Uint8Array | null
|
|
4
|
+
export type SqlParams = readonly SqlValue[] | Readonly<Record<string, SqlValue>>
|
|
5
|
+
|
|
6
|
+
export type SqlResult = Readonly<{
|
|
7
|
+
changes: number
|
|
8
|
+
lastInsertRowId?: number | bigint
|
|
9
|
+
}>
|
|
10
|
+
|
|
11
|
+
export type SqlDatabase = Readonly<{
|
|
12
|
+
all<Row>(sql: string, params?: SqlParams): Promise<readonly Row[]>
|
|
13
|
+
get<Row>(sql: string, params?: SqlParams): Promise<Row | undefined>
|
|
14
|
+
run(sql: string, params?: SqlParams): Promise<SqlResult>
|
|
15
|
+
exec(sql: string): Promise<void>
|
|
16
|
+
transaction<T>(run: (database: SqlDatabase) => Promise<T>): Promise<T>
|
|
17
|
+
}>
|
|
18
|
+
|
|
19
|
+
export type SqlConnection = SqlDatabase & Readonly<{ close(): Promise<void> }>
|
|
20
|
+
|
|
21
|
+
export type DatabaseMigration = Readonly<{
|
|
22
|
+
id: string
|
|
23
|
+
sql: string
|
|
24
|
+
}>
|
|
25
|
+
|
|
26
|
+
export type PluginDatabase = Readonly<{
|
|
27
|
+
migrations?: readonly DatabaseMigration[]
|
|
28
|
+
}>
|
|
29
|
+
|
|
30
|
+
const MIGRATION_ID = /^[a-z0-9]+(?:[-_.][a-z0-9]+)*$/
|
|
31
|
+
const MIGRATIONS_TABLE = "_pathmx_migrations"
|
|
32
|
+
|
|
33
|
+
async function checksum(text: string) {
|
|
34
|
+
const bytes = new TextEncoder().encode(text)
|
|
35
|
+
const digest = await crypto.subtle.digest("SHA-256", bytes)
|
|
36
|
+
return [...new Uint8Array(digest)]
|
|
37
|
+
.map((value) => value.toString(16).padStart(2, "0"))
|
|
38
|
+
.join("")
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function validateMigrations(migrations: readonly DatabaseMigration[]) {
|
|
42
|
+
const ids = new Set<string>()
|
|
43
|
+
for (const migration of migrations) {
|
|
44
|
+
if (!MIGRATION_ID.test(migration.id)) {
|
|
45
|
+
throw new Error(`Invalid database migration id: ${migration.id}`)
|
|
46
|
+
}
|
|
47
|
+
if (ids.has(migration.id)) {
|
|
48
|
+
throw new Error(`Duplicate database migration id: ${migration.id}`)
|
|
49
|
+
}
|
|
50
|
+
if (!migration.sql.trim()) {
|
|
51
|
+
throw new Error(`Database migration ${migration.id} is empty.`)
|
|
52
|
+
}
|
|
53
|
+
ids.add(migration.id)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function migrate(
|
|
58
|
+
plugin: string,
|
|
59
|
+
database: SqlConnection,
|
|
60
|
+
migrations: readonly DatabaseMigration[],
|
|
61
|
+
) {
|
|
62
|
+
validateMigrations(migrations)
|
|
63
|
+
await database.transaction(async (transaction) => {
|
|
64
|
+
await transaction.exec(`
|
|
65
|
+
CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (
|
|
66
|
+
id TEXT PRIMARY KEY,
|
|
67
|
+
checksum TEXT NOT NULL,
|
|
68
|
+
applied_at TEXT NOT NULL
|
|
69
|
+
)
|
|
70
|
+
`)
|
|
71
|
+
const applied = await transaction.all<{ id: string; checksum: string }>(
|
|
72
|
+
`SELECT id, checksum FROM ${MIGRATIONS_TABLE} ORDER BY rowid`,
|
|
73
|
+
)
|
|
74
|
+
for (const [index, current] of applied.entries()) {
|
|
75
|
+
const migration = migrations[index]
|
|
76
|
+
if (!migration || migration.id !== current.id) {
|
|
77
|
+
throw new Error(`Database migration history changed: ${plugin}`)
|
|
78
|
+
}
|
|
79
|
+
const expected = await checksum(migration.sql)
|
|
80
|
+
if (current.checksum !== expected) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Applied database migration changed: ${plugin}/${migration.id}`,
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const migration of migrations.slice(applied.length)) {
|
|
87
|
+
const expected = await checksum(migration.sql)
|
|
88
|
+
await transaction.exec(migration.sql)
|
|
89
|
+
await transaction.run(
|
|
90
|
+
`INSERT INTO ${MIGRATIONS_TABLE} (id, checksum, applied_at) VALUES (?, ?, ?)`,
|
|
91
|
+
[migration.id, expected, new Date().toISOString()],
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function lazyDatabase(open: () => Promise<SqlConnection>): SqlDatabase {
|
|
98
|
+
return Object.freeze({
|
|
99
|
+
all: <Row>(sql: string, params?: SqlParams) =>
|
|
100
|
+
open().then((database) => database.all<Row>(sql, params)),
|
|
101
|
+
get: <Row>(sql: string, params?: SqlParams) =>
|
|
102
|
+
open().then((database) => database.get<Row>(sql, params)),
|
|
103
|
+
run: (sql: string, params?: SqlParams) =>
|
|
104
|
+
open().then((database) => database.run(sql, params)),
|
|
105
|
+
exec: (sql: string) => open().then((database) => database.exec(sql)),
|
|
106
|
+
transaction: <T>(run: (database: SqlDatabase) => Promise<T>) =>
|
|
107
|
+
open().then((database) => database.transaction(run)),
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type RegisteredDatabase = {
|
|
112
|
+
handle: SqlDatabase
|
|
113
|
+
connection(): Promise<SqlConnection> | undefined
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Own lazy, isolated plugin databases and their migration lifecycle. */
|
|
117
|
+
export class PluginDatabases {
|
|
118
|
+
private registered = new Map<string, RegisteredDatabase>()
|
|
119
|
+
private closing?: Promise<void>
|
|
120
|
+
|
|
121
|
+
constructor(
|
|
122
|
+
private environment: PathMXDatabaseEnvironment | undefined,
|
|
123
|
+
private stateRoot: string,
|
|
124
|
+
) {}
|
|
125
|
+
|
|
126
|
+
prepare(definition: PluginDatabase) {
|
|
127
|
+
const migrations = (definition.migrations ?? []).map((migration) =>
|
|
128
|
+
Object.freeze({ ...migration }),
|
|
129
|
+
)
|
|
130
|
+
validateMigrations(migrations)
|
|
131
|
+
return Object.freeze({ migrations: Object.freeze(migrations) })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
register(plugin: string, definition: PluginDatabase) {
|
|
135
|
+
const prepared = this.prepare(definition)
|
|
136
|
+
if (this.registered.has(plugin)) {
|
|
137
|
+
throw new Error(`Duplicate plugin database: ${plugin}`)
|
|
138
|
+
}
|
|
139
|
+
let connection: Promise<SqlConnection> | undefined
|
|
140
|
+
const registered: RegisteredDatabase = {
|
|
141
|
+
handle: lazyDatabase(
|
|
142
|
+
() => (connection ??= this.create(plugin, prepared)),
|
|
143
|
+
),
|
|
144
|
+
connection: () => connection,
|
|
145
|
+
}
|
|
146
|
+
this.registered.set(plugin, registered)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
get(plugin: string) {
|
|
150
|
+
return this.registered.get(plugin)?.handle
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private async create(plugin: string, definition: PluginDatabase) {
|
|
154
|
+
if (!this.environment) {
|
|
155
|
+
throw new Error(
|
|
156
|
+
`Plugin ${plugin} requires SQL database support in this environment.`,
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
const connection = await this.environment.open(
|
|
160
|
+
this.environment.path(this.stateRoot, `${plugin}.sqlite`),
|
|
161
|
+
)
|
|
162
|
+
try {
|
|
163
|
+
await migrate(plugin, connection, definition.migrations ?? [])
|
|
164
|
+
return connection
|
|
165
|
+
} catch (error) {
|
|
166
|
+
await connection.close().catch(() => undefined)
|
|
167
|
+
throw error
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private async closeAll() {
|
|
172
|
+
const connections = [...this.registered.values()]
|
|
173
|
+
.map((registered) => registered.connection())
|
|
174
|
+
.filter(
|
|
175
|
+
(connection): connection is Promise<SqlConnection> => !!connection,
|
|
176
|
+
)
|
|
177
|
+
const settled = await Promise.allSettled(
|
|
178
|
+
connections.map(async (connection) => (await connection).close()),
|
|
179
|
+
)
|
|
180
|
+
const rejected = settled.find(
|
|
181
|
+
(result): result is PromiseRejectedResult => result.status === "rejected",
|
|
182
|
+
)
|
|
183
|
+
if (rejected) throw rejected.reason
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
close() {
|
|
187
|
+
return (this.closing ??= this.closeAll())
|
|
188
|
+
}
|
|
189
|
+
}
|
package/dependencies.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type Dependency =
|
|
2
|
+
| Readonly<{ type: "source"; path: string }>
|
|
3
|
+
| Readonly<{ type: "plugin"; plugin: string; key: string }>
|
|
4
|
+
|
|
5
|
+
export type DependencyContext = {
|
|
6
|
+
depend(dependency: Dependency): void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function dependencyKey(dependency: Dependency) {
|
|
10
|
+
if (dependency.type === "source") return dependency.path
|
|
11
|
+
return `plugin:${dependency.plugin}:${dependency.key}`
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class PageDependencies implements DependencyContext {
|
|
15
|
+
readonly keys = new Set<string>()
|
|
16
|
+
|
|
17
|
+
depend = (dependency: Dependency) => {
|
|
18
|
+
this.keys.add(dependencyKey(dependency))
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Bidirectional compile-time dependency index. Replacement is authoritative. */
|
|
23
|
+
export class DependencyIndex {
|
|
24
|
+
private outgoing = new Map<string, Set<string>>()
|
|
25
|
+
private incoming = new Map<string, Set<string>>()
|
|
26
|
+
|
|
27
|
+
replaceDependent(dependent: string, dependencies: Iterable<string>) {
|
|
28
|
+
this.deleteDependent(dependent)
|
|
29
|
+
const deps = new Set(dependencies)
|
|
30
|
+
if (!deps.size) return
|
|
31
|
+
this.outgoing.set(dependent, deps)
|
|
32
|
+
for (const dependency of deps) {
|
|
33
|
+
let dependents = this.incoming.get(dependency)
|
|
34
|
+
if (!dependents) {
|
|
35
|
+
dependents = new Set()
|
|
36
|
+
this.incoming.set(dependency, dependents)
|
|
37
|
+
}
|
|
38
|
+
dependents.add(dependent)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
deleteDependent(dependent: string) {
|
|
43
|
+
const deps = this.outgoing.get(dependent)
|
|
44
|
+
if (!deps) return
|
|
45
|
+
for (const dependency of deps) {
|
|
46
|
+
const dependents = this.incoming.get(dependency)
|
|
47
|
+
dependents?.delete(dependent)
|
|
48
|
+
if (dependents && dependents.size === 0) this.incoming.delete(dependency)
|
|
49
|
+
}
|
|
50
|
+
this.outgoing.delete(dependent)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
dependentsOf(dependency: string) {
|
|
54
|
+
return this.incoming.get(dependency) ?? new Set<string>()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
dependenciesOf(dependent: string) {
|
|
58
|
+
return this.outgoing.get(dependent) ?? new Set<string>()
|
|
59
|
+
}
|
|
60
|
+
}
|