@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.
Files changed (195) hide show
  1. package/LICENSE.md +172 -0
  2. package/README.md +111 -0
  3. package/actions/actions.ts +307 -0
  4. package/actions/draft.ts +105 -0
  5. package/actions/transaction.ts +164 -0
  6. package/assets.ts +149 -0
  7. package/authority/access.ts +820 -0
  8. package/authority/actors.ts +271 -0
  9. package/authority/agents.ts +95 -0
  10. package/authority/credentials.ts +144 -0
  11. package/authority/model.ts +71 -0
  12. package/authority/sharing.ts +89 -0
  13. package/browser-bundle.ts +61 -0
  14. package/canonical.ts +41 -0
  15. package/cli/dev.ts +134 -0
  16. package/cli/export.ts +74 -0
  17. package/cli/index.ts +52 -0
  18. package/cli/lint.ts +164 -0
  19. package/cli/mv.ts +30 -0
  20. package/cli/perms.ts +182 -0
  21. package/cli/plugin-packages.ts +159 -0
  22. package/cli/plugins.ts +95 -0
  23. package/cli/query.ts +225 -0
  24. package/cli/readme.md +95 -0
  25. package/cli/release.ts +75 -0
  26. package/cli/rm.ts +22 -0
  27. package/cli/serve.ts +100 -0
  28. package/cli/tree.ts +131 -0
  29. package/cli/update.ts +78 -0
  30. package/cli/utils.ts +53 -0
  31. package/core.ts +96 -0
  32. package/database.ts +189 -0
  33. package/dependencies.ts +60 -0
  34. package/dist/pathmx.js +1224 -0
  35. package/document.ts +194 -0
  36. package/environment.ts +110 -0
  37. package/environments/bun-plugins.ts +68 -0
  38. package/environments/bun-publish.ts +194 -0
  39. package/environments/bun.ts +223 -0
  40. package/globals.d.ts +14 -0
  41. package/graph.ts +153 -0
  42. package/host/compiler.ts +314 -0
  43. package/host/create.ts +66 -0
  44. package/host/engine.ts +630 -0
  45. package/host/plugin-host.ts +487 -0
  46. package/host/render.ts +102 -0
  47. package/host/response.ts +20 -0
  48. package/host/shell.html +15 -0
  49. package/host/source-sync.ts +254 -0
  50. package/html-attributes.ts +15 -0
  51. package/html.ts +48 -0
  52. package/icons/LICENSE +43 -0
  53. package/icons/lucide.ts +54 -0
  54. package/index.ts +75 -0
  55. package/invalidation.ts +85 -0
  56. package/log.ts +84 -0
  57. package/ops/href.ts +87 -0
  58. package/ops/index.ts +9 -0
  59. package/ops/move-source.ts +114 -0
  60. package/ops/plan.ts +47 -0
  61. package/ops/remove-source.ts +70 -0
  62. package/package.json +46 -0
  63. package/path.ts +75 -0
  64. package/pattern.ts +314 -0
  65. package/perf.ts +88 -0
  66. package/plugins/actor-path.ts +43 -0
  67. package/plugins/actor-profile.ts +55 -0
  68. package/plugins/agent-browsing.ts +183 -0
  69. package/plugins/app-component.ts +23 -0
  70. package/plugins/block-layout/index.ts +78 -0
  71. package/plugins/block-layout/theme.css +90 -0
  72. package/plugins/callout/index.ts +236 -0
  73. package/plugins/callout/theme.css +138 -0
  74. package/plugins/canonical-links.ts +144 -0
  75. package/plugins/code-highlight/client.ts +89 -0
  76. package/plugins/code-highlight/index.ts +147 -0
  77. package/plugins/code-highlight/theme.css +89 -0
  78. package/plugins/context.ts +131 -0
  79. package/plugins/datatype/Datatype[wdth,wght].woff2 +0 -0
  80. package/plugins/datatype/OFL.txt +96 -0
  81. package/plugins/datatype/index.ts +287 -0
  82. package/plugins/definition-list.ts +236 -0
  83. package/plugins/directive-definitions.ts +13 -0
  84. package/plugins/embedding/client.ts +88 -0
  85. package/plugins/embedding/index.ts +43 -0
  86. package/plugins/image/index.ts +68 -0
  87. package/plugins/image/theme.css +43 -0
  88. package/plugins/include.ts +119 -0
  89. package/plugins/index.ts +91 -0
  90. package/plugins/input/authoring.ts +236 -0
  91. package/plugins/input/config.ts +85 -0
  92. package/plugins/input/index.ts +359 -0
  93. package/plugins/input/model.ts +173 -0
  94. package/plugins/input/state.ts +272 -0
  95. package/plugins/layout.ts +313 -0
  96. package/plugins/literate/client.ts +208 -0
  97. package/plugins/literate/index.ts +742 -0
  98. package/plugins/math/index.ts +140 -0
  99. package/plugins/meta.ts +42 -0
  100. package/plugins/navigation/index.ts +158 -0
  101. package/plugins/navigation/model.ts +364 -0
  102. package/plugins/preset.ts +65 -0
  103. package/plugins/query/index.ts +322 -0
  104. package/plugins/rss/index.ts +46 -0
  105. package/plugins/runtime/actions.ts +62 -0
  106. package/plugins/runtime/client.ts +8 -0
  107. package/plugins/runtime/document.ts +506 -0
  108. package/plugins/runtime/error.ts +154 -0
  109. package/plugins/runtime/heading-links.ts +31 -0
  110. package/plugins/runtime/index.ts +104 -0
  111. package/plugins/runtime/live.ts +51 -0
  112. package/plugins/runtime/navigation.ts +113 -0
  113. package/plugins/runtime/pending.ts +53 -0
  114. package/plugins/runtime/publication.ts +19 -0
  115. package/plugins/runtime/start.ts +177 -0
  116. package/plugins/runtime/static-client.ts +6 -0
  117. package/plugins/runtime/static-live.ts +188 -0
  118. package/plugins/runtime/view-transition.ts +72 -0
  119. package/plugins/runtime/viewport.ts +110 -0
  120. package/plugins/schema.ts +24 -0
  121. package/plugins/scripts.ts +140 -0
  122. package/plugins/source-preview/client.ts +256 -0
  123. package/plugins/source-preview/index.ts +132 -0
  124. package/plugins/source-preview/model.ts +11 -0
  125. package/plugins/source-preview/preview.css +107 -0
  126. package/plugins/styles.ts +145 -0
  127. package/plugins/theme/components.ts +10 -0
  128. package/plugins/theme/foundation.css +358 -0
  129. package/plugins/theme/index.ts +333 -0
  130. package/plugins/theme/prose.css +284 -0
  131. package/plugins/theme/scheme.js +63 -0
  132. package/plugins/theme/toggle.css +44 -0
  133. package/plugins/theme/view-transition.css +68 -0
  134. package/plugins/toc/client.ts +146 -0
  135. package/plugins/toc/index.ts +232 -0
  136. package/plugins/toc/theme.css +172 -0
  137. package/plugins/types.ts +538 -0
  138. package/problem.ts +41 -0
  139. package/project.ts +6 -0
  140. package/publish/build.ts +402 -0
  141. package/publish/concurrent.ts +32 -0
  142. package/publish/identity.ts +63 -0
  143. package/publish/index.ts +11 -0
  144. package/publish/location.ts +167 -0
  145. package/publish/model.ts +58 -0
  146. package/publish/plan.ts +300 -0
  147. package/publish/validate.ts +150 -0
  148. package/query/candidate-index.ts +101 -0
  149. package/query/dependency-index.ts +99 -0
  150. package/query/fields.ts +216 -0
  151. package/query/invalidation.ts +48 -0
  152. package/query/kernel.ts +776 -0
  153. package/query/model.ts +79 -0
  154. package/repository.ts +374 -0
  155. package/runtime-profile.ts +34 -0
  156. package/schema/model.ts +92 -0
  157. package/schema/registry.ts +642 -0
  158. package/schema/standard.ts +226 -0
  159. package/server/context.ts +94 -0
  160. package/server/css-imports.ts +66 -0
  161. package/server/http.ts +333 -0
  162. package/server/page.ts +21 -0
  163. package/server/protocols.ts +12 -0
  164. package/server/representations.ts +66 -0
  165. package/server/router.ts +131 -0
  166. package/server/routes.ts +202 -0
  167. package/server/serve.ts +5 -0
  168. package/server/watch.ts +120 -0
  169. package/source/blocks.ts +71 -0
  170. package/source/format.ts +164 -0
  171. package/source/index.ts +67 -0
  172. package/source/interpolate.ts +63 -0
  173. package/source/markdown/blocks.ts +46 -0
  174. package/source/markdown/comments.ts +32 -0
  175. package/source/markdown/fences.ts +67 -0
  176. package/source/markdown/headings.ts +88 -0
  177. package/source/markdown/index.ts +34 -0
  178. package/source/markdown/inline-code.ts +6 -0
  179. package/source/markdown/links.ts +326 -0
  180. package/source/markdown/lists.ts +223 -0
  181. package/source/markdown/prose.ts +74 -0
  182. package/source/markdown/scan.ts +79 -0
  183. package/source/markdown/text.ts +82 -0
  184. package/source/meta.ts +87 -0
  185. package/source/source.ts +121 -0
  186. package/source/topmatter.ts +57 -0
  187. package/source/utils.ts +21 -0
  188. package/source-diagnostic.ts +63 -0
  189. package/text-file.ts +26 -0
  190. package/util.ts +35 -0
  191. package/view/components.ts +499 -0
  192. package/view/model.ts +91 -0
  193. package/view/projection.ts +162 -0
  194. package/view/session.ts +146 -0
  195. package/view/syntax.ts +372 -0
@@ -0,0 +1,538 @@
1
+ import type { ReadAccess, SourceCapability } from "../authority/access.ts"
2
+ import type { GraphEdge } from "../graph.ts"
3
+ import type { Html } from "../html.ts"
4
+ import type { RouteInvalidation } from "../invalidation.ts"
5
+ import type { CommitResult } from "../repository.ts"
6
+ import type { RuntimeProfile } from "../runtime-profile.ts"
7
+ import type { Source, SourceBlock, SourceNode } from "../source/index.ts"
8
+ import type {
9
+ Actor,
10
+ ActorProfile,
11
+ CredentialSession,
12
+ Principal,
13
+ } from "../authority/model.ts"
14
+ import type { Dependency, DependencyContext } from "../dependencies.ts"
15
+ import type { PathMXDataSchema, StandardSchemaV1 } from "../schema/standard.ts"
16
+ import type { CredentialHost } from "../authority/credentials.ts"
17
+ import type { SourceTransaction } from "../actions/transaction.ts"
18
+ import type { View } from "../view/model.ts"
19
+ import type { AgentAdmission, AgentJob } from "../authority/agents.ts"
20
+ import type { Log } from "../log.ts"
21
+ import type { PluginDatabase, SqlDatabase } from "../database.ts"
22
+ import type { Command } from "commander"
23
+ import type { ApplicationSchema, SchemaContribution } from "../schema/model.ts"
24
+ import type { PatternInstaller } from "../pattern.ts"
25
+ import type {
26
+ HtmlElement,
27
+ HtmlStringRewrite,
28
+ PathMXYaml,
29
+ } from "../environment.ts"
30
+ import type { PreparedQueryPlan } from "../query/kernel.ts"
31
+ import type { QueryRequest } from "../query/model.ts"
32
+
33
+ export type { Actor, Source }
34
+ export type { Dependency, DependencyContext }
35
+
36
+ export type Awaitable<T> = T | Promise<T>
37
+
38
+ export type CompileContext = DependencyContext & {
39
+ source: Source
40
+ block?: SourceBlock
41
+ /** Compiler-owned placement of this content in the rendered document. */
42
+ scope: "page" | "embedded"
43
+ slots: string[]
44
+ /** Authored element names found once for the owning Source. */
45
+ elements: ReadonlySet<string>
46
+ }
47
+
48
+ export type DocumentStylesheet =
49
+ | Readonly<{ type: "stylesheet"; href: string; media?: string }>
50
+ | Readonly<{
51
+ type: "stylesheet"
52
+ name: string
53
+ text: string
54
+ media?: string
55
+ }>
56
+
57
+ export type DocumentStyle = Readonly<{
58
+ type: "style"
59
+ text: string
60
+ }>
61
+
62
+ export type DocumentPreload = Readonly<{
63
+ type: "preload"
64
+ href: string
65
+ as: string
66
+ contentType?: string
67
+ crossorigin?: boolean
68
+ }>
69
+
70
+ export type DocumentScript =
71
+ | Readonly<{ type: "script"; href: string; module?: boolean }>
72
+ | Readonly<{ type: "script"; source: string; module?: boolean }>
73
+ | Readonly<{ type: "script"; text: string; module?: boolean }>
74
+ | Readonly<{ type: "script"; name: string; text: string; module?: boolean }>
75
+
76
+ export type DocumentTitle = Readonly<{ type: "title"; text: string }>
77
+
78
+ /** `attrs` render verbatim: `{ charset }`, `{ name, content }`, `{ name, media, content }`. */
79
+ export type DocumentMeta = Readonly<{
80
+ type: "meta"
81
+ attrs: Readonly<Record<string, string>>
82
+ }>
83
+
84
+ /** Linked hrefs may be internal (`/app.js`) or external (`https://…`). */
85
+ export type DocumentAsset =
86
+ | DocumentStylesheet
87
+ | DocumentStyle
88
+ | DocumentPreload
89
+ | DocumentScript
90
+ | DocumentTitle
91
+ | DocumentMeta
92
+
93
+ export type AssetContext = {
94
+ addAsset(...assets: DocumentAsset[]): void
95
+ /** Bundle one browser entrypoint and atomically contribute its JS/CSS outputs. */
96
+ addBrowserBundle(entrypoint: string | URL, name: string): Promise<void>
97
+ }
98
+
99
+ export type PageContext = DependencyContext &
100
+ AssetContext & {
101
+ source: Source
102
+ }
103
+
104
+ export type SourceTransition = Readonly<{
105
+ before?: Source
106
+ after?: Source
107
+ }>
108
+
109
+ export type PluginInvalidationTarget =
110
+ | Readonly<{
111
+ type: "route"
112
+ route: string
113
+ actors?: Iterable<string>
114
+ }>
115
+ | Readonly<{
116
+ type: "actor"
117
+ actors: Iterable<string>
118
+ }>
119
+
120
+ export type PluginInvalidation = Readonly<{
121
+ /** Cached pages whose compiled output or dependency records are stale. */
122
+ dependencies?: Iterable<Dependency>
123
+ sourceIds?: Iterable<string>
124
+ /** Connected routes that must reconcile without implying a rebuild. */
125
+ targets?: Iterable<PluginInvalidationTarget>
126
+ }>
127
+
128
+ export type CompiledPage = Readonly<{
129
+ html: string
130
+ assets: readonly DocumentAsset[]
131
+ sourceId: string
132
+ route: string
133
+ }>
134
+
135
+ export type DocumentAssetContext = AssetContext & {
136
+ page?: CompiledPage
137
+ }
138
+
139
+ export type ViewContext = Readonly<{
140
+ view: View
141
+ session: CredentialSession
142
+ /** @deprecated Prefer `view.principal`; retained during the View migration. */
143
+ principal: Principal
144
+ /** @deprecated Internal render Source; migrate safe reads to `view`. */
145
+ source: Source
146
+ route: string
147
+ /** Database isolated to the View Component's owning plugin. */
148
+ database?: SqlDatabase
149
+ }>
150
+
151
+ type ViewComponentUseBase = Readonly<{
152
+ props: Readonly<Record<string, string>>
153
+ origin: Source
154
+ }>
155
+
156
+ export type ViewComponentUse<Data = undefined> = ViewComponentUseBase &
157
+ ([Data] extends [undefined]
158
+ ? Readonly<{ data?: undefined }>
159
+ : Readonly<{ data: Data }>)
160
+
161
+ export type ViewComponent<
162
+ Compiled = undefined,
163
+ DataSchema extends PathMXDataSchema | undefined = undefined,
164
+ > = Readonly<{
165
+ /** Internal `plugin:name` identity; authored Sources use the generated `x-plugin-name` tag. */
166
+ tag: string
167
+ /** Browser entrypoint bundled only on pages that author this component. */
168
+ client?: string | URL
169
+ /** Validate authored element attributes before exposing the raw props. */
170
+ props?: StandardSchemaV1
171
+ /** Validate one structured record supplied by a data-aware renderer. */
172
+ data?: DataSchema
173
+ compile?(
174
+ use: ViewComponentUse,
175
+ ctx: PageContext,
176
+ app: AppContext,
177
+ ): Awaitable<Compiled>
178
+ render(
179
+ use: ViewComponentUse<
180
+ DataSchema extends PathMXDataSchema
181
+ ? StandardSchemaV1.InferOutput<DataSchema>
182
+ : undefined
183
+ >,
184
+ compiled: Compiled,
185
+ ctx: ViewContext,
186
+ app: AppContext,
187
+ ): Awaitable<Html>
188
+ }>
189
+
190
+ /** Type-erased storage boundary; use defineViewComponent for authored inference. */
191
+ export type AnyViewComponent = Readonly<{
192
+ tag: string
193
+ client?: string | URL
194
+ props?: StandardSchemaV1
195
+ data?: PathMXDataSchema
196
+ compile?: (...args: any[]) => Awaitable<unknown>
197
+ render: (...args: any[]) => Awaitable<Html>
198
+ }>
199
+
200
+ /** @internal Runtime-authored component definition used by Literate Components. */
201
+ export type AuthoredDataComponent = Readonly<{
202
+ tag: string
203
+ available(origin: Source): boolean
204
+ compile?(
205
+ origin: Source,
206
+ ctx: PageContext,
207
+ app: AppContext,
208
+ ): Awaitable<unknown>
209
+ render(
210
+ use: ViewComponentUse<unknown>,
211
+ compiled: unknown,
212
+ ctx: ViewContext,
213
+ app: AppContext,
214
+ ): Awaitable<Html>
215
+ }>
216
+
217
+ export type ViewVariableUse = Readonly<{
218
+ path: readonly string[]
219
+ origin: Source
220
+ }>
221
+
222
+ export type ViewVariableValue = string | number | boolean | null | undefined
223
+
224
+ export type ViewVariable = Readonly<{
225
+ namespace: string
226
+ render(
227
+ use: ViewVariableUse,
228
+ ctx: ViewContext,
229
+ app: AppContext,
230
+ ): Awaitable<ViewVariableValue>
231
+ }>
232
+
233
+ export type ServerGraph = Readonly<{
234
+ linksFrom(path: string): readonly GraphEdge[]
235
+ linksTo(path: string): readonly GraphEdge[]
236
+ neighbors(path: string): readonly SourceNode[]
237
+ backlinks(path: string): readonly SourceNode[]
238
+ }>
239
+
240
+ export type ServerRenderOptions = Readonly<{
241
+ status?: number
242
+ headers?: HeadersInit
243
+ }>
244
+
245
+ export type PluginLog = Readonly<Pick<Log, "debug" | "info" | "warn" | "error">>
246
+
247
+ export type PluginContext = Readonly<{
248
+ plugin: string
249
+ log: PluginLog
250
+ runtime: RuntimeProfile
251
+ database?: SqlDatabase
252
+ }>
253
+
254
+ type AppSources = Readonly<{
255
+ /** Resolve a Source id, exact Source path, or public route. */
256
+ get(idOrPath: string): Source | undefined
257
+ all(): readonly Source[]
258
+ /** Resolve only a public page route. */
259
+ resolve(pathname: string): Source | undefined
260
+ /** Normalize a repository-relative value to an absolute graph path. */
261
+ path(value: string): string
262
+ isPage(source: Source): boolean
263
+ builtPages(): readonly CompiledPage[]
264
+ }>
265
+
266
+ type AppActors = Readonly<{
267
+ profile(actorId: string): ActorProfile | undefined
268
+ homeActorId(path: string): string | undefined
269
+ }>
270
+
271
+ type AppAccess = Readonly<{
272
+ read(principal: Principal, target: Source | string): ReadAccess
273
+ can(
274
+ principal: Principal,
275
+ target: Source | string,
276
+ capability: SourceCapability,
277
+ ): boolean
278
+ canEmbed(container: string, dependency: string): boolean
279
+ }>
280
+
281
+ type AppAssets = Readonly<{
282
+ load(url: URL): Awaitable<Blob>
283
+ publish(name: string, blob: Blob, contentType?: string): Promise<string>
284
+ }>
285
+
286
+ type AppHtml = Readonly<{
287
+ rewrite: HtmlStringRewrite
288
+ }>
289
+
290
+ type AppCompiler = Readonly<{
291
+ block(
292
+ source: Source,
293
+ block: SourceBlock,
294
+ dependencies?: DependencyContext,
295
+ options?: { id?: string },
296
+ ): Promise<string>
297
+ /** Compile another Source as a document while excluding the calling Plugin. */
298
+ document(
299
+ source: Source,
300
+ dependencies: DependencyContext,
301
+ ): Promise<Readonly<{ html: string; assets: readonly DocumentAsset[] }>>
302
+ }>
303
+
304
+ type AppComponents = Readonly<{
305
+ claim(sourcePath: string, tags: Iterable<string>): void
306
+ release(sourcePath: string): void
307
+ has(tag: string): boolean
308
+ /** @internal Replace runtime-authored structured-data component definitions. */
309
+ replaceData(
310
+ sourcePath: string,
311
+ components: Iterable<AuthoredDataComponent>,
312
+ ): void
313
+ /** @internal Prepare one structured-data component once during page compile. */
314
+ prepareData(
315
+ tag: string,
316
+ props: Readonly<Record<string, string>>,
317
+ origin: Source,
318
+ ctx: PageContext,
319
+ ): Promise<unknown>
320
+ /** @internal Render one validated record through a prepared component. */
321
+ renderData(prepared: unknown, data: unknown, ctx: ViewContext): Promise<Html>
322
+ }>
323
+
324
+ type AppDiagnostics = Readonly<{
325
+ /** Stop indexing one invalid Source with a lint-collectable Plugin issue. */
326
+ invalid(source: Source, message: string): never
327
+ }>
328
+
329
+ type AppQuery = Readonly<{
330
+ /** @internal Normalize, validate, and track a compile-time query plan. */
331
+ prepare(
332
+ query: string,
333
+ request?: QueryRequest<unknown>,
334
+ ): Promise<PreparedQueryPlan>
335
+ }>
336
+
337
+ /** Request-independent capabilities available to Plugin hooks and components. */
338
+ export type AppContext = PluginContext &
339
+ Readonly<{
340
+ schema: ApplicationSchema
341
+ query: AppQuery
342
+ yaml: PathMXYaml
343
+ sources: AppSources
344
+ graph: ServerGraph
345
+ actors: AppActors
346
+ access: AppAccess
347
+ assets: AppAssets
348
+ html: AppHtml
349
+ compile: AppCompiler
350
+ components: AppComponents
351
+ diagnostics: AppDiagnostics
352
+ }>
353
+
354
+ export type PluginCommandContext = Readonly<{
355
+ plugin: string
356
+ pathsRoot: string
357
+ /** Explicit CLI Action root, when it differs from the project convention. */
358
+ actionsRoot?: string
359
+ projectRoot: string
360
+ stateRoot: string
361
+ log: PluginLog
362
+ database?: SqlDatabase
363
+ /** Plan and install user-owned project files through the portable Pattern boundary. */
364
+ patterns: PatternInstaller
365
+ }>
366
+
367
+ export type PublishContext = Readonly<{
368
+ /** Add one finite public GET pathname to the static publication. */
369
+ route(pathname: string): void
370
+ /** Reject publication when an authored use has no static representation. */
371
+ unsupported(source: Source, message: string): void
372
+ }>
373
+
374
+ export type ServerContext = Readonly<{
375
+ request: Request
376
+ url: URL
377
+ /** Route path relative to the owning plugin's `/api/<plugin-id>/` mount. */
378
+ path: string
379
+ plugin: string
380
+ /** Structured logger whose messages are prefixed with the owning Plugin id. */
381
+ log: PluginLog
382
+ runtime: RuntimeProfile
383
+ /** Database isolated to the route's owning plugin. */
384
+ database?: SqlDatabase
385
+ graph: ServerGraph
386
+ source(idOrPath: string): Source | undefined
387
+ sources(): readonly Source[]
388
+ resolve(pathname: string): Source | undefined
389
+ isPublic(source: Source | string): boolean
390
+ session(): Promise<CredentialSession>
391
+ /** Return the memoized viewer-filtered Source and graph projection. */
392
+ view(): Promise<View>
393
+ readAccess(source: Source | string): Promise<ReadAccess>
394
+ onSync(listener: (event: RouteInvalidation) => void): () => void
395
+ render(
396
+ source: Source | string,
397
+ options?: ServerRenderOptions,
398
+ ): Promise<Response>
399
+ mutate(
400
+ run: (transaction: SourceTransaction) => Awaitable<void>,
401
+ ): Promise<CommitResult>
402
+ /** Commit one idempotent, target-only mutation under an admitted Agent Actor. */
403
+ mutateAsAgent(admission: AgentAdmission, run: AgentJob): Promise<CommitResult>
404
+ /** Delegate an auth URL to the CredentialHost owned by the auth plugin. */
405
+ handleCredentials(): Promise<Response>
406
+ }>
407
+
408
+ export type ServerRouteHandler = (ctx: ServerContext) => Awaitable<Response>
409
+
410
+ export type ServerRoute = Readonly<{
411
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "*"
412
+ path: string
413
+ handle: ServerRouteHandler
414
+ }>
415
+
416
+ export type StreamHandler = {
417
+ element(el: HtmlElement): void
418
+ }
419
+
420
+ const PLUGIN_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
421
+
422
+ export function canonicalPluginId(id: string) {
423
+ if (!PLUGIN_ID.test(id)) throw new Error(`Invalid plugin id: ${id}`)
424
+ return id
425
+ }
426
+
427
+ /** Optional lifecycle hooks. Implement any subset. */
428
+ export type Plugin = {
429
+ /** Stable build-plugin identity used for diagnostics and composition. */
430
+ id: string
431
+ /** Earlier-installed Plugin ids this Plugin cannot operate without. */
432
+ requires?: readonly string[]
433
+ /** Discoverable Source, Block, prop, query, and Action declarations. */
434
+ schema?: SchemaContribution
435
+ /** Optional isolated SQL state owned and migrated by this plugin. */
436
+ database?: PluginDatabase
437
+ /** Trusted local administrator commands mounted below `pmx <plugin-id>`. */
438
+ commands?(group: Command, ctx: PluginCommandContext): void
439
+ /** Optional request-admission provider owned by this plugin. */
440
+ credentials?: CredentialHost
441
+ /** Portable HTTP handlers. Routes run before credential resolution. */
442
+ routes?: readonly ServerRoute[]
443
+ /** Viewer-aware leaf components authored through this plugin's namespace. */
444
+ components?: readonly AnyViewComponent[]
445
+ /** Escaped scalar namespaces resolved while rendering an admitted view. */
446
+ variables?: readonly ViewVariable[]
447
+ /** Perf/diagnostic label; defaults to the factory function's name. */
448
+ name?: string
449
+ /** Initialize plugin-owned resources after registration and before indexing. */
450
+ setup?(ctx: PluginContext): Awaitable<void>
451
+ /** Release plugin-owned resources during application shutdown. */
452
+ close?(ctx: PluginContext): Awaitable<void>
453
+ /** Report Source-local repository issues when `pmx lint` explicitly runs. */
454
+ lint?(
455
+ report: (source: Source, message: string) => void,
456
+ app: AppContext,
457
+ ): Awaitable<void>
458
+ /** Contribute finite GET routes and static-compatibility diagnostics. */
459
+ publish?(ctx: PublishContext, app: AppContext): Awaitable<void>
460
+ /** Inspect or enrich a source when it is added to the index. */
461
+ index?(source: Source, app: AppContext): void
462
+ /** Remove plugin state associated with a source before reindexing or deletion. */
463
+ unindex?(sourcePath: string, app: AppContext): void
464
+ /** Transform interpolated Markdown before it is rendered to HTML. */
465
+ precompile?(
466
+ markdown: string,
467
+ ctx: CompileContext,
468
+ app: AppContext,
469
+ ): Awaitable<string>
470
+ /** Transform HTML immediately after rendering a source or block. */
471
+ postcompile?(
472
+ html: string,
473
+ ctx: CompileContext,
474
+ app: AppContext,
475
+ ): Awaitable<string>
476
+ /** Transform an assembled page and collect its page-specific assets. */
477
+ page?(html: string, ctx: PageContext, app: AppContext): Awaitable<string>
478
+ /** Describe pages affected by plugin-owned state after a Source transition. */
479
+ invalidate?(
480
+ transition: SourceTransition,
481
+ app: AppContext,
482
+ ): PluginInvalidation | undefined
483
+ /** Collect document-level assets, optionally using the compiled page. */
484
+ documentAssets?(ctx: DocumentAssetContext, app: AppContext): Awaitable<void>
485
+ /** Register selector handlers for streaming transformations of final HTML. */
486
+ streamSelectors?(
487
+ ctx: ViewContext,
488
+ app: AppContext,
489
+ ): Record<string, StreamHandler>
490
+ }
491
+
492
+ /** Create a per-Engine plugin instance. Stateful plugins keep data in the closure. */
493
+ export type PluginFactory = () => Plugin
494
+
495
+ /** Preserve inference for repository-authored build plugins. */
496
+ export function definePlugin<const Definition extends Plugin>(
497
+ definition: Definition,
498
+ ) {
499
+ return definition
500
+ }
501
+
502
+ /** Preserve compile-result inference for a viewer-aware component. */
503
+ export function defineViewComponent<
504
+ Compiled,
505
+ DataSchema extends PathMXDataSchema | undefined = undefined,
506
+ >(view: ViewComponent<Compiled, DataSchema>) {
507
+ return view
508
+ }
509
+
510
+ /** Define one actor-aware scalar namespace rendered through opaque view slots. */
511
+ export function defineViewVariable(variable: ViewVariable) {
512
+ return variable
513
+ }
514
+
515
+ function serverRoute(
516
+ method: ServerRoute["method"],
517
+ path: string,
518
+ handle: ServerRouteHandler,
519
+ ): ServerRoute {
520
+ return { method, path, handle }
521
+ }
522
+
523
+ function methodRoute(method: Exclude<ServerRoute["method"], "*">) {
524
+ return (path: string, handle: ServerRouteHandler) =>
525
+ serverRoute(method, path, handle)
526
+ }
527
+
528
+ /** Define relative routes mounted below `/api/<plugin-id>/`. */
529
+ export const route = Object.freeze({
530
+ get: methodRoute("GET"),
531
+ post: methodRoute("POST"),
532
+ put: methodRoute("PUT"),
533
+ patch: methodRoute("PATCH"),
534
+ delete: methodRoute("DELETE"),
535
+ options: methodRoute("OPTIONS"),
536
+ all: (path: string, handle: ServerRouteHandler) =>
537
+ serverRoute("*", path, handle),
538
+ })
package/problem.ts ADDED
@@ -0,0 +1,41 @@
1
+ export type ProblemDetails = {
2
+ type: "about:blank"
3
+ title: string
4
+ status: number
5
+ detail: string
6
+ }
7
+
8
+ export function actionProblem(
9
+ req: Request,
10
+ status: 400 | 403 | 404,
11
+ detail: string,
12
+ ) {
13
+ if (!req.headers.get("accept")?.includes("application/problem+json")) {
14
+ return new Response(detail, {
15
+ status,
16
+ headers: {
17
+ "Content-Type": "text/plain; charset=utf-8",
18
+ Vary: "Accept",
19
+ },
20
+ })
21
+ }
22
+
23
+ const problem: ProblemDetails = {
24
+ type: "about:blank",
25
+ title:
26
+ status === 404
27
+ ? "Not Found"
28
+ : status === 403
29
+ ? "Forbidden"
30
+ : "Bad Request",
31
+ status,
32
+ detail,
33
+ }
34
+ return new Response(JSON.stringify(problem), {
35
+ status,
36
+ headers: {
37
+ "Content-Type": "application/problem+json",
38
+ Vary: "Accept",
39
+ },
40
+ })
41
+ }
package/project.ts ADDED
@@ -0,0 +1,6 @@
1
+ /** Resolve a conventional project directory beside the authored `paths/` root. */
2
+ export function projectDirectory(pathsRoot: string, name: string) {
3
+ const root = pathsRoot.replaceAll("\\", "/").replace(/\/+$/, "")
4
+ const slash = root.lastIndexOf("/")
5
+ return `${slash === -1 ? "" : root.slice(0, slash + 1)}${name}`
6
+ }