@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
package/view/syntax.ts ADDED
@@ -0,0 +1,372 @@
1
+ import type { HtmlStringRewrite } from "../environment.ts"
2
+ import { parseHtmlAttributes } from "../html-attributes.ts"
3
+ import type { CompileContext } from "../plugins/types.ts"
4
+ import {
5
+ transformMarkdownProse,
6
+ transformOutsideHtmlComments,
7
+ } from "../source/markdown/prose.ts"
8
+
9
+ const NAME = "[a-z][a-z0-9]*(?:-[a-z0-9]+)*"
10
+ const VIEW_ELEMENT = new RegExp("^" + NAME + ":" + NAME + "$")
11
+ const AUTHORED_VIEW_ELEMENT = new RegExp("^x-" + NAME + "$")
12
+ const LITERATE_ELEMENT = /^[a-z][\w.-]*$/
13
+ export const VIEW_USE_TAG = "pmx-view"
14
+ export const VIEW_NAME_ATTR = "data-pmx-view"
15
+ export const VIEW_SLOT_ATTR = "data-pmx-view-slot"
16
+ const ATTRS = "((?:\\s+(?:[^<>\\\"']|\\\"[^\\\"]*\\\"|'[^']*')*)?)"
17
+ const AUTHORED_VIEW_USE = new RegExp(
18
+ "<(x-" + NAME + ")" + ATTRS + "\\s*(?:/\\s*>|>\\s*</\\1\\s*>)",
19
+ "gi",
20
+ )
21
+ const REMAINING_AUTHORED_VIEW_USE = new RegExp(
22
+ "<(x-" + NAME + ")(?:\\s|/?>)",
23
+ "i",
24
+ )
25
+ const VIEW_SYNTAX_CANDIDATE = /<x-[a-z]/i
26
+ const COMPONENT_SYNTAX_SLOT = /<!--pmx-component-syntax:(\d+)-->/g
27
+ const VIEW_VARIABLE_USE =
28
+ /\{\{\s*([A-Za-z_][\w-]*)\.([A-Za-z_][\w-]*(?:\.[A-Za-z_][\w-]*)*)\s*\}\}/g
29
+ export const COMPONENT_ORIGIN_ATTR = "data-pmx-component-origin"
30
+
31
+ function escapeRegExp(value: string) {
32
+ return value.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")
33
+ }
34
+
35
+ export function isViewComponentTag(tag: string) {
36
+ return VIEW_ELEMENT.test(tag)
37
+ }
38
+
39
+ export function isAuthoredViewTag(tag: string) {
40
+ return AUTHORED_VIEW_ELEMENT.test(tag)
41
+ }
42
+
43
+ export function isLiterateComponentTag(tag: string) {
44
+ return LITERATE_ELEMENT.test(tag)
45
+ }
46
+
47
+ /** Map a plugin-owned identity to its standards-compatible authored tag. */
48
+ export function authoredViewTag(tag: string) {
49
+ if (!isViewComponentTag(tag)) {
50
+ throw new Error("Invalid View Component tag: " + tag)
51
+ }
52
+ return "x-" + tag.replace(":", "-")
53
+ }
54
+
55
+ export function viewUsePattern() {
56
+ const escaped = escapeRegExp(VIEW_USE_TAG)
57
+ const attrs = "((?:\\s+(?:[^<>\\\"']|\\\"[^\\\"]*\\\"|'[^']*')*)?)"
58
+ return new RegExp(
59
+ "<" + escaped + attrs + "\\s*(?:/\\s*>|>\\s*</" + escaped + "\\s*>)",
60
+ "gi",
61
+ )
62
+ }
63
+
64
+ export function hasRemainingViewUse(html: string) {
65
+ return new RegExp("<" + VIEW_USE_TAG + "(?:\\s|/?>)", "i").test(html)
66
+ }
67
+
68
+ function compileVisibleViewUses(markup: string, sourcePath: string) {
69
+ const compiled = markup.replace(
70
+ AUTHORED_VIEW_USE,
71
+ (_match, tag: string, attrs: string) =>
72
+ "<" +
73
+ VIEW_USE_TAG +
74
+ " " +
75
+ VIEW_NAME_ATTR +
76
+ '="' +
77
+ tag +
78
+ '"' +
79
+ attrs +
80
+ " />",
81
+ )
82
+ const remaining = compiled.match(REMAINING_AUTHORED_VIEW_USE)
83
+ if (remaining) {
84
+ throw new Error(
85
+ "<" +
86
+ remaining[1] +
87
+ "> must be empty or self-closing in " +
88
+ sourcePath +
89
+ ".",
90
+ )
91
+ }
92
+ return compiled
93
+ }
94
+
95
+ export function compileViewUses(markup: string, sourcePath: string) {
96
+ return transformOutsideHtmlComments(markup, (visible) =>
97
+ compileVisibleViewUses(visible, sourcePath),
98
+ )
99
+ }
100
+
101
+ function stashLiterateUses(
102
+ markup: string,
103
+ tags: readonly string[],
104
+ slots: string[],
105
+ ) {
106
+ if (!tags.length) return markup
107
+ const names = tags.map(escapeRegExp).join("|")
108
+ const uses = new RegExp("<\\/?(?:" + names + ")" + ATTRS + "\\s*\\/?>", "gi")
109
+ return markup.replace(uses, (use) => {
110
+ const slot = slots.push(use) - 1
111
+ return "<!--pmx-component-syntax:" + slot + "-->"
112
+ })
113
+ }
114
+
115
+ /** Compile authored syntax without touching comments, fences, or inline code. */
116
+ function compileComponentSyntax(
117
+ markdown: string,
118
+ sourcePath: string,
119
+ literateTags: readonly string[] = [],
120
+ slots: string[] = [],
121
+ ) {
122
+ return transformMarkdownProse(markdown, (markup) =>
123
+ stashLiterateUses(compileViewUses(markup, sourcePath), literateTags, slots),
124
+ )
125
+ }
126
+
127
+ export type AuthoredViewComponentUse = Readonly<{
128
+ tag: string
129
+ props: Readonly<Record<string, string>>
130
+ }>
131
+
132
+ /** Read empty authored View Component uses without touching code examples. */
133
+ export function authoredViewComponentUses(markdown: string) {
134
+ const uses: AuthoredViewComponentUse[] = []
135
+ transformMarkdownProse(markdown, (markup) => {
136
+ for (const match of markup.matchAll(
137
+ new RegExp(AUTHORED_VIEW_USE.source, "gi"),
138
+ )) {
139
+ uses.push(
140
+ Object.freeze({
141
+ tag: match[1]!.toLowerCase(),
142
+ props: Object.freeze(parseHtmlAttributes(match[2])),
143
+ }),
144
+ )
145
+ }
146
+ return markup
147
+ })
148
+ return Object.freeze(uses)
149
+ }
150
+
151
+ export function elementTagNames(markup: string) {
152
+ const tags = new Set<string>()
153
+ for (const match of markup.matchAll(/<([a-z][\w:.-]*)\b/gi)) {
154
+ if (match[1]) tags.add(match[1].toLowerCase())
155
+ }
156
+ return tags
157
+ }
158
+
159
+ /** Attach trusted origin metadata before authored components are expanded. */
160
+ export function stampComponentOrigins(
161
+ html: string,
162
+ originPath: string,
163
+ authoredTags: Pick<ReadonlySet<string>, "has">,
164
+ rewriteHtml: HtmlStringRewrite,
165
+ ) {
166
+ return rewriteHtml(html, [
167
+ [
168
+ "*",
169
+ {
170
+ element(element) {
171
+ if (element.tagName === "pmx-content") return
172
+ if (
173
+ element.tagName === VIEW_USE_TAG ||
174
+ element.tagName.includes(":") ||
175
+ element.tagName.includes("-") ||
176
+ authoredTags.has(element.tagName)
177
+ ) {
178
+ element.setAttribute(COMPONENT_ORIGIN_ATTR, originPath)
179
+ }
180
+ },
181
+ },
182
+ ],
183
+ ])
184
+ }
185
+
186
+ /** Attach attributes to the first root in trusted rendered HTML. */
187
+ export function stampHtmlRoot(
188
+ html: string,
189
+ attrs: Readonly<Record<string, string>>,
190
+ rewriteHtml: HtmlStringRewrite,
191
+ ) {
192
+ let stamped = false
193
+ return rewriteHtml(html, [
194
+ [
195
+ "*",
196
+ {
197
+ element(element) {
198
+ if (stamped) return
199
+ stamped = true
200
+ for (const [name, value] of Object.entries(attrs)) {
201
+ element.setAttribute(name, value)
202
+ }
203
+ },
204
+ },
205
+ ],
206
+ ])
207
+ }
208
+
209
+ /** Mark the first rendered root as the semantic boundary of a component. */
210
+ export function stampComponentRoot(
211
+ html: string,
212
+ tag: string,
213
+ rewriteHtml: HtmlStringRewrite,
214
+ attrs?: Record<string, string>,
215
+ ) {
216
+ return stampHtmlRoot(
217
+ html,
218
+ {
219
+ "data-component": tag,
220
+ ...(attrs ? { "data-pmx-attrs": JSON.stringify(attrs) } : {}),
221
+ },
222
+ rewriteHtml,
223
+ )
224
+ }
225
+
226
+ export function stripComponentOrigins(html: string) {
227
+ if (!html.includes(COMPONENT_ORIGIN_ATTR)) return html
228
+ return html.replace(
229
+ new RegExp("\\s" + COMPONENT_ORIGIN_ATTR + '="[^"]*"', "g"),
230
+ "",
231
+ )
232
+ }
233
+
234
+ /** Stateful authored syntax indexes kept separate from component execution. */
235
+ export class ViewComponentSyntax {
236
+ private variables = new Map<string, string>()
237
+ private variableMatcher?: RegExp
238
+ private authoredTags = new Map<string, string>()
239
+ private authoredMatcher?: RegExp
240
+ private slots = new WeakMap<CompileContext, string[]>()
241
+
242
+ registerVariables(variables: Iterable<readonly [string, string]>) {
243
+ let changed = false
244
+ for (const [namespace, tag] of variables) {
245
+ this.variables.set(namespace, tag)
246
+ changed = true
247
+ }
248
+ if (!changed) return
249
+ this.variableMatcher = new RegExp(
250
+ "\\{\\{\\s*(?:" +
251
+ [...this.variables.keys()].map(escapeRegExp).join("|") +
252
+ ")\\.",
253
+ )
254
+ }
255
+
256
+ compileVariables(markdown: string) {
257
+ if (!this.variableMatcher?.test(markdown)) return markdown
258
+ return transformMarkdownProse(markdown, (prose) =>
259
+ prose.replace(
260
+ VIEW_VARIABLE_USE,
261
+ (token, namespace: string, path: string) => {
262
+ const tag = this.variables.get(namespace)
263
+ return tag
264
+ ? "<" +
265
+ VIEW_USE_TAG +
266
+ " " +
267
+ VIEW_NAME_ATTR +
268
+ '="' +
269
+ tag +
270
+ '" data-pmx-variable="' +
271
+ path +
272
+ '" />'
273
+ : token
274
+ },
275
+ ),
276
+ )
277
+ }
278
+
279
+ compile(markdown: string, ctx: CompileContext) {
280
+ if (!markdown.includes("<")) return markdown
281
+ const hasViewSyntax = VIEW_SYNTAX_CANDIDATE.test(markdown)
282
+ const hasLiterateSyntax = this.authoredMatcher?.test(markdown) ?? false
283
+ if (!hasViewSyntax && !hasLiterateSyntax) return markdown
284
+ const slots: string[] = []
285
+ const compiled = compileComponentSyntax(
286
+ markdown,
287
+ ctx.source.path,
288
+ hasLiterateSyntax ? [...this.authoredTags.keys()] : [],
289
+ slots,
290
+ )
291
+ if (slots.length) this.slots.set(ctx, slots)
292
+ return compiled
293
+ }
294
+
295
+ restore(html: string, ctx: CompileContext) {
296
+ const slots = this.slots.get(ctx)
297
+ if (!slots) return html
298
+ this.slots.delete(ctx)
299
+ return html.replace(
300
+ COMPONENT_SYNTAX_SLOT,
301
+ (marker, index: string) => slots[Number(index)] ?? marker,
302
+ )
303
+ }
304
+
305
+ replaceAuthored(sourcePath: string, tags: Iterable<string>) {
306
+ const pending = new Set(tags)
307
+ for (const tag of pending) {
308
+ if (!isLiterateComponentTag(tag)) {
309
+ throw new Error("Invalid Literate Component tag: " + tag)
310
+ }
311
+ if (tag.startsWith("x-")) {
312
+ throw new Error(
313
+ "Literate Component tag <" + tag + "> uses the reserved x- prefix.",
314
+ )
315
+ }
316
+ const authoredBy = this.authoredTags.get(tag)
317
+ if (authoredBy && authoredBy !== sourcePath) {
318
+ throw new Error(
319
+ "Duplicate Literate Component <" +
320
+ tag +
321
+ "> in " +
322
+ sourcePath +
323
+ " and " +
324
+ authoredBy +
325
+ ".",
326
+ )
327
+ }
328
+ }
329
+ for (const [tag, owner] of this.authoredTags) {
330
+ if (owner === sourcePath) this.authoredTags.delete(tag)
331
+ }
332
+ for (const tag of pending) this.authoredTags.set(tag, sourcePath)
333
+ this.rebuildMatcher()
334
+ }
335
+
336
+ deleteAuthored(sourcePath: string) {
337
+ let deleted = false
338
+ for (const [tag, owner] of this.authoredTags) {
339
+ if (owner !== sourcePath) continue
340
+ this.authoredTags.delete(tag)
341
+ deleted = true
342
+ }
343
+ if (deleted) this.rebuildMatcher()
344
+ }
345
+
346
+ has(tag: string) {
347
+ return this.authoredTags.has(tag)
348
+ }
349
+
350
+ stampOrigins(
351
+ html: string,
352
+ originPath: string,
353
+ rewriteHtml: HtmlStringRewrite,
354
+ ) {
355
+ return stampComponentOrigins(
356
+ html,
357
+ originPath,
358
+ this.authoredTags,
359
+ rewriteHtml,
360
+ )
361
+ }
362
+
363
+ private rebuildMatcher() {
364
+ const tags = [...this.authoredTags.keys()]
365
+ this.authoredMatcher = tags.length
366
+ ? new RegExp(
367
+ "<(?:" + tags.map(escapeRegExp).join("|") + ")(?:\\s|/?>)",
368
+ "i",
369
+ )
370
+ : undefined
371
+ }
372
+ }