@pathmx/core 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +172 -0
- package/README.md +111 -0
- package/actions/actions.ts +307 -0
- package/actions/draft.ts +105 -0
- package/actions/transaction.ts +164 -0
- package/assets.ts +149 -0
- package/authority/access.ts +820 -0
- package/authority/actors.ts +271 -0
- package/authority/agents.ts +95 -0
- package/authority/credentials.ts +144 -0
- package/authority/model.ts +71 -0
- package/authority/sharing.ts +89 -0
- package/browser-bundle.ts +61 -0
- package/canonical.ts +41 -0
- package/cli/dev.ts +134 -0
- package/cli/export.ts +74 -0
- package/cli/index.ts +52 -0
- package/cli/lint.ts +164 -0
- package/cli/mv.ts +30 -0
- package/cli/perms.ts +182 -0
- package/cli/plugin-packages.ts +159 -0
- package/cli/plugins.ts +95 -0
- package/cli/query.ts +225 -0
- package/cli/readme.md +95 -0
- package/cli/release.ts +75 -0
- package/cli/rm.ts +22 -0
- package/cli/serve.ts +100 -0
- package/cli/tree.ts +131 -0
- package/cli/update.ts +78 -0
- package/cli/utils.ts +53 -0
- package/core.ts +96 -0
- package/database.ts +189 -0
- package/dependencies.ts +60 -0
- package/dist/pathmx.js +1224 -0
- package/document.ts +194 -0
- package/environment.ts +110 -0
- package/environments/bun-plugins.ts +68 -0
- package/environments/bun-publish.ts +194 -0
- package/environments/bun.ts +223 -0
- package/globals.d.ts +14 -0
- package/graph.ts +153 -0
- package/host/compiler.ts +314 -0
- package/host/create.ts +66 -0
- package/host/engine.ts +630 -0
- package/host/plugin-host.ts +487 -0
- package/host/render.ts +102 -0
- package/host/response.ts +20 -0
- package/host/shell.html +15 -0
- package/host/source-sync.ts +254 -0
- package/html-attributes.ts +15 -0
- package/html.ts +48 -0
- package/icons/LICENSE +43 -0
- package/icons/lucide.ts +54 -0
- package/index.ts +75 -0
- package/invalidation.ts +85 -0
- package/log.ts +84 -0
- package/ops/href.ts +87 -0
- package/ops/index.ts +9 -0
- package/ops/move-source.ts +114 -0
- package/ops/plan.ts +47 -0
- package/ops/remove-source.ts +70 -0
- package/package.json +46 -0
- package/path.ts +75 -0
- package/pattern.ts +314 -0
- package/perf.ts +88 -0
- package/plugins/actor-path.ts +43 -0
- package/plugins/actor-profile.ts +55 -0
- package/plugins/agent-browsing.ts +183 -0
- package/plugins/app-component.ts +23 -0
- package/plugins/block-layout/index.ts +78 -0
- package/plugins/block-layout/theme.css +90 -0
- package/plugins/callout/index.ts +236 -0
- package/plugins/callout/theme.css +138 -0
- package/plugins/canonical-links.ts +144 -0
- package/plugins/code-highlight/client.ts +89 -0
- package/plugins/code-highlight/index.ts +147 -0
- package/plugins/code-highlight/theme.css +89 -0
- package/plugins/context.ts +131 -0
- package/plugins/datatype/Datatype[wdth,wght].woff2 +0 -0
- package/plugins/datatype/OFL.txt +96 -0
- package/plugins/datatype/index.ts +287 -0
- package/plugins/definition-list.ts +236 -0
- package/plugins/directive-definitions.ts +13 -0
- package/plugins/embedding/client.ts +88 -0
- package/plugins/embedding/index.ts +43 -0
- package/plugins/image/index.ts +68 -0
- package/plugins/image/theme.css +43 -0
- package/plugins/include.ts +119 -0
- package/plugins/index.ts +91 -0
- package/plugins/input/authoring.ts +236 -0
- package/plugins/input/config.ts +85 -0
- package/plugins/input/index.ts +359 -0
- package/plugins/input/model.ts +173 -0
- package/plugins/input/state.ts +272 -0
- package/plugins/layout.ts +313 -0
- package/plugins/literate/client.ts +208 -0
- package/plugins/literate/index.ts +742 -0
- package/plugins/math/index.ts +140 -0
- package/plugins/meta.ts +42 -0
- package/plugins/navigation/index.ts +158 -0
- package/plugins/navigation/model.ts +364 -0
- package/plugins/preset.ts +65 -0
- package/plugins/query/index.ts +322 -0
- package/plugins/rss/index.ts +46 -0
- package/plugins/runtime/actions.ts +62 -0
- package/plugins/runtime/client.ts +8 -0
- package/plugins/runtime/document.ts +506 -0
- package/plugins/runtime/error.ts +154 -0
- package/plugins/runtime/heading-links.ts +31 -0
- package/plugins/runtime/index.ts +104 -0
- package/plugins/runtime/live.ts +51 -0
- package/plugins/runtime/navigation.ts +113 -0
- package/plugins/runtime/pending.ts +53 -0
- package/plugins/runtime/publication.ts +19 -0
- package/plugins/runtime/start.ts +177 -0
- package/plugins/runtime/static-client.ts +6 -0
- package/plugins/runtime/static-live.ts +188 -0
- package/plugins/runtime/view-transition.ts +72 -0
- package/plugins/runtime/viewport.ts +110 -0
- package/plugins/schema.ts +24 -0
- package/plugins/scripts.ts +140 -0
- package/plugins/source-preview/client.ts +256 -0
- package/plugins/source-preview/index.ts +132 -0
- package/plugins/source-preview/model.ts +11 -0
- package/plugins/source-preview/preview.css +107 -0
- package/plugins/styles.ts +145 -0
- package/plugins/theme/components.ts +10 -0
- package/plugins/theme/foundation.css +358 -0
- package/plugins/theme/index.ts +333 -0
- package/plugins/theme/prose.css +284 -0
- package/plugins/theme/scheme.js +63 -0
- package/plugins/theme/toggle.css +44 -0
- package/plugins/theme/view-transition.css +68 -0
- package/plugins/toc/client.ts +146 -0
- package/plugins/toc/index.ts +232 -0
- package/plugins/toc/theme.css +172 -0
- package/plugins/types.ts +538 -0
- package/problem.ts +41 -0
- package/project.ts +6 -0
- package/publish/build.ts +402 -0
- package/publish/concurrent.ts +32 -0
- package/publish/identity.ts +63 -0
- package/publish/index.ts +11 -0
- package/publish/location.ts +167 -0
- package/publish/model.ts +58 -0
- package/publish/plan.ts +300 -0
- package/publish/validate.ts +150 -0
- package/query/candidate-index.ts +101 -0
- package/query/dependency-index.ts +99 -0
- package/query/fields.ts +216 -0
- package/query/invalidation.ts +48 -0
- package/query/kernel.ts +776 -0
- package/query/model.ts +79 -0
- package/repository.ts +374 -0
- package/runtime-profile.ts +34 -0
- package/schema/model.ts +92 -0
- package/schema/registry.ts +642 -0
- package/schema/standard.ts +226 -0
- package/server/context.ts +94 -0
- package/server/css-imports.ts +66 -0
- package/server/http.ts +333 -0
- package/server/page.ts +21 -0
- package/server/protocols.ts +12 -0
- package/server/representations.ts +66 -0
- package/server/router.ts +131 -0
- package/server/routes.ts +202 -0
- package/server/serve.ts +5 -0
- package/server/watch.ts +120 -0
- package/source/blocks.ts +71 -0
- package/source/format.ts +164 -0
- package/source/index.ts +67 -0
- package/source/interpolate.ts +63 -0
- package/source/markdown/blocks.ts +46 -0
- package/source/markdown/comments.ts +32 -0
- package/source/markdown/fences.ts +67 -0
- package/source/markdown/headings.ts +88 -0
- package/source/markdown/index.ts +34 -0
- package/source/markdown/inline-code.ts +6 -0
- package/source/markdown/links.ts +326 -0
- package/source/markdown/lists.ts +223 -0
- package/source/markdown/prose.ts +74 -0
- package/source/markdown/scan.ts +79 -0
- package/source/markdown/text.ts +82 -0
- package/source/meta.ts +87 -0
- package/source/source.ts +121 -0
- package/source/topmatter.ts +57 -0
- package/source/utils.ts +21 -0
- package/source-diagnostic.ts +63 -0
- package/text-file.ts +26 -0
- package/util.ts +35 -0
- package/view/components.ts +499 -0
- package/view/model.ts +91 -0
- package/view/projection.ts +162 -0
- package/view/session.ts +146 -0
- package/view/syntax.ts +372 -0
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
import type { ActionContribution } from "../actions/actions.ts"
|
|
2
|
+
import type {
|
|
3
|
+
ApplicationSchema,
|
|
4
|
+
BlockType,
|
|
5
|
+
DataProp,
|
|
6
|
+
NodeSchema,
|
|
7
|
+
NodeType,
|
|
8
|
+
NodeTypeDefinition,
|
|
9
|
+
QuerySchema,
|
|
10
|
+
SchemaContribution,
|
|
11
|
+
SourceType,
|
|
12
|
+
} from "./model.ts"
|
|
13
|
+
import type { QueryContribution } from "../query/model.ts"
|
|
14
|
+
import {
|
|
15
|
+
formatSchemaIssues,
|
|
16
|
+
requireStandardJsonSchema,
|
|
17
|
+
type PathMXDataSchema,
|
|
18
|
+
type StandardSchemaV1,
|
|
19
|
+
} from "./standard.ts"
|
|
20
|
+
import type { Source, SourceNode } from "../source/index.ts"
|
|
21
|
+
import { SourceDiagnosticError } from "../source-diagnostic.ts"
|
|
22
|
+
|
|
23
|
+
const NODE_TYPE_ID = /^[a-z0-9]+(?:[/-][a-z0-9]+)*$/
|
|
24
|
+
const MEMBER_ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/
|
|
25
|
+
|
|
26
|
+
type JsonSchema = Readonly<Record<string, unknown>>
|
|
27
|
+
|
|
28
|
+
type TypeContribution = Readonly<{
|
|
29
|
+
type: string
|
|
30
|
+
plugin: string
|
|
31
|
+
definition: NodeTypeDefinition
|
|
32
|
+
inputJsonSchema?: JsonSchema
|
|
33
|
+
outputJsonSchema?: JsonSchema
|
|
34
|
+
}>
|
|
35
|
+
|
|
36
|
+
type PropContribution = DataProp
|
|
37
|
+
|
|
38
|
+
export type RegisteredQuery = Readonly<{
|
|
39
|
+
id: string
|
|
40
|
+
name: string
|
|
41
|
+
plugin: string
|
|
42
|
+
definition: QueryContribution
|
|
43
|
+
input?: JsonSchema
|
|
44
|
+
output: JsonSchema
|
|
45
|
+
}>
|
|
46
|
+
|
|
47
|
+
export type PreparedSchema = Readonly<{
|
|
48
|
+
sources: readonly TypeContribution[]
|
|
49
|
+
blocks: readonly TypeContribution[]
|
|
50
|
+
props: readonly PropContribution[]
|
|
51
|
+
queries: readonly RegisteredQuery[]
|
|
52
|
+
actions: readonly (readonly [string, ActionContribution])[]
|
|
53
|
+
}>
|
|
54
|
+
|
|
55
|
+
function compareText(left: string, right: string) {
|
|
56
|
+
return left < right ? -1 : left > right ? 1 : 0
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function titleCase(type: string) {
|
|
60
|
+
const name = type.split("/").at(-1) ?? type
|
|
61
|
+
return name
|
|
62
|
+
.replace(/[-_]+/g, " ")
|
|
63
|
+
.replace(/\b\w/g, (character) => character.toUpperCase())
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function normalizedText(value: unknown, field: string, owner: string) {
|
|
67
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
68
|
+
throw new Error(`${owner} ${field} must be a non-empty string.`)
|
|
69
|
+
}
|
|
70
|
+
return value.trim()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function prepareType(
|
|
74
|
+
node: "Source" | "Block",
|
|
75
|
+
plugin: string,
|
|
76
|
+
type: string,
|
|
77
|
+
definition: NodeTypeDefinition,
|
|
78
|
+
): TypeContribution {
|
|
79
|
+
if (!NODE_TYPE_ID.test(type)) {
|
|
80
|
+
throw new Error(`Plugin ${plugin} has an invalid ${node} type: ${type}`)
|
|
81
|
+
}
|
|
82
|
+
if (!definition || typeof definition !== "object") {
|
|
83
|
+
throw new Error(`Plugin ${plugin} ${node} type ${type} must be an object.`)
|
|
84
|
+
}
|
|
85
|
+
const owner = `${plugin}:${type}`
|
|
86
|
+
const schemaSubject = `Plugin ${plugin} ${node} type ${type} schema`
|
|
87
|
+
const input = definition.schema
|
|
88
|
+
? requireStandardJsonSchema(definition.schema, "input", schemaSubject)
|
|
89
|
+
: undefined
|
|
90
|
+
const output = definition.schema
|
|
91
|
+
? requireStandardJsonSchema(definition.schema, "output", schemaSubject)
|
|
92
|
+
: undefined
|
|
93
|
+
const normalized = Object.freeze({
|
|
94
|
+
...(definition.label !== undefined
|
|
95
|
+
? { label: normalizedText(definition.label, "label", owner) }
|
|
96
|
+
: {}),
|
|
97
|
+
...(definition.description !== undefined
|
|
98
|
+
? {
|
|
99
|
+
description: normalizedText(
|
|
100
|
+
definition.description,
|
|
101
|
+
"description",
|
|
102
|
+
owner,
|
|
103
|
+
),
|
|
104
|
+
}
|
|
105
|
+
: {}),
|
|
106
|
+
...(definition.icon !== undefined
|
|
107
|
+
? { icon: normalizedText(definition.icon, "icon", owner) }
|
|
108
|
+
: {}),
|
|
109
|
+
...(definition.color !== undefined
|
|
110
|
+
? { color: normalizedText(definition.color, "color", owner) }
|
|
111
|
+
: {}),
|
|
112
|
+
...(definition.schema ? { schema: definition.schema } : {}),
|
|
113
|
+
})
|
|
114
|
+
if (!Object.keys(normalized).length) {
|
|
115
|
+
throw new Error(
|
|
116
|
+
`Plugin ${plugin} ${node} type ${type} must contribute presentation or a schema.`,
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
return Object.freeze({
|
|
120
|
+
type,
|
|
121
|
+
plugin,
|
|
122
|
+
definition: normalized,
|
|
123
|
+
...(input ? { inputJsonSchema: input } : {}),
|
|
124
|
+
...(output ? { outputJsonSchema: output } : {}),
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function prepareProp(
|
|
129
|
+
plugin: string,
|
|
130
|
+
name: string,
|
|
131
|
+
schema: PathMXDataSchema,
|
|
132
|
+
): PropContribution {
|
|
133
|
+
if (!MEMBER_ID.test(name)) {
|
|
134
|
+
throw new Error(`Plugin ${plugin} has an invalid data prop: ${name}`)
|
|
135
|
+
}
|
|
136
|
+
const subject = `Plugin ${plugin} data prop ${name}`
|
|
137
|
+
const input = requireStandardJsonSchema(schema, "input", subject)
|
|
138
|
+
const output = requireStandardJsonSchema(schema, "output", subject)
|
|
139
|
+
return Object.freeze({
|
|
140
|
+
name,
|
|
141
|
+
plugin,
|
|
142
|
+
schema,
|
|
143
|
+
inputJsonSchema: input,
|
|
144
|
+
outputJsonSchema: output,
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function prepareQuery(
|
|
149
|
+
plugin: string,
|
|
150
|
+
name: string,
|
|
151
|
+
definition: QueryContribution,
|
|
152
|
+
): RegisteredQuery {
|
|
153
|
+
if (!MEMBER_ID.test(name)) {
|
|
154
|
+
throw new Error(`Plugin ${plugin} has an invalid query name: ${name}`)
|
|
155
|
+
}
|
|
156
|
+
if (!definition || typeof definition !== "object") {
|
|
157
|
+
throw new Error(`Plugin query ${plugin}:${name} must be an object.`)
|
|
158
|
+
}
|
|
159
|
+
if (typeof definition.resolve !== "function") {
|
|
160
|
+
throw new Error(`Plugin query ${plugin}:${name} must define resolve().`)
|
|
161
|
+
}
|
|
162
|
+
const id = `${plugin}:${name}`
|
|
163
|
+
const output = requireStandardJsonSchema(
|
|
164
|
+
definition.output,
|
|
165
|
+
"output",
|
|
166
|
+
`Plugin query ${id} output`,
|
|
167
|
+
)
|
|
168
|
+
const input = definition.input
|
|
169
|
+
? requireStandardJsonSchema(
|
|
170
|
+
definition.input,
|
|
171
|
+
"input",
|
|
172
|
+
`Plugin query ${id} input`,
|
|
173
|
+
)
|
|
174
|
+
: undefined
|
|
175
|
+
return Object.freeze({
|
|
176
|
+
id,
|
|
177
|
+
name,
|
|
178
|
+
plugin,
|
|
179
|
+
definition,
|
|
180
|
+
...(input ? { input } : {}),
|
|
181
|
+
output,
|
|
182
|
+
})
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function prepareAction(
|
|
186
|
+
plugin: string,
|
|
187
|
+
name: string,
|
|
188
|
+
action: ActionContribution,
|
|
189
|
+
) {
|
|
190
|
+
if (!MEMBER_ID.test(name)) {
|
|
191
|
+
throw new Error(`Plugin ${plugin} has an invalid Action name: ${name}`)
|
|
192
|
+
}
|
|
193
|
+
if (typeof action !== "function") {
|
|
194
|
+
throw new Error(`Plugin Action ${plugin}:${name} must be a function.`)
|
|
195
|
+
}
|
|
196
|
+
if (action.input) {
|
|
197
|
+
requireStandardJsonSchema(
|
|
198
|
+
action.input,
|
|
199
|
+
"input",
|
|
200
|
+
`Plugin Action ${plugin}:${name} input`,
|
|
201
|
+
)
|
|
202
|
+
}
|
|
203
|
+
if (action.output) {
|
|
204
|
+
requireStandardJsonSchema(
|
|
205
|
+
action.output,
|
|
206
|
+
"output",
|
|
207
|
+
`Plugin Action ${plugin}:${name} output`,
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
return Object.freeze([`${plugin}:${name}`, action] as const)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
214
|
+
return (
|
|
215
|
+
value != null &&
|
|
216
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
217
|
+
typeof (value as { then?: unknown }).then === "function"
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function validate(
|
|
222
|
+
source: Source,
|
|
223
|
+
node: SourceNode,
|
|
224
|
+
plugin: string,
|
|
225
|
+
subject: string,
|
|
226
|
+
schema: StandardSchemaV1,
|
|
227
|
+
value: unknown,
|
|
228
|
+
) {
|
|
229
|
+
const result = schema["~standard"].validate(value)
|
|
230
|
+
const pending = isPromiseLike(result)
|
|
231
|
+
if (pending) void Promise.resolve(result).catch(() => undefined)
|
|
232
|
+
const message = pending
|
|
233
|
+
? "application schemas must validate authored data synchronously"
|
|
234
|
+
: result.issues?.length
|
|
235
|
+
? formatSchemaIssues(result.issues)
|
|
236
|
+
: undefined
|
|
237
|
+
return message
|
|
238
|
+
? new SourceDiagnosticError({
|
|
239
|
+
source: source.path,
|
|
240
|
+
plugin,
|
|
241
|
+
message: `Invalid ${subject} on ${node.path}: ${message}`,
|
|
242
|
+
})
|
|
243
|
+
: undefined
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function report(
|
|
247
|
+
error: SourceDiagnosticError | undefined,
|
|
248
|
+
onError?: (error: unknown) => boolean,
|
|
249
|
+
) {
|
|
250
|
+
if (error && !onError?.(error)) throw error
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Ordered Plugin Schema registry and canonical Source/Block data validator. */
|
|
254
|
+
export class SchemaRegistry {
|
|
255
|
+
private sourceContributions = new Map<string, TypeContribution[]>()
|
|
256
|
+
private blockContributions = new Map<string, TypeContribution[]>()
|
|
257
|
+
private sourceDescriptors = new Map<string, SourceType>()
|
|
258
|
+
private blockDescriptors = new Map<string, BlockType>()
|
|
259
|
+
private observedSourceDescriptors = new Map<string, SourceType>()
|
|
260
|
+
private observedBlockDescriptors = new Map<string, BlockType>()
|
|
261
|
+
private propContributions = new Map<string, PropContribution>()
|
|
262
|
+
private queryContributions = new Map<string, RegisteredQuery>()
|
|
263
|
+
private queryReferences = new WeakMap<object, RegisteredQuery>()
|
|
264
|
+
private queryDescriptions = new Map<string, QuerySchema>()
|
|
265
|
+
|
|
266
|
+
prepare(plugin: string, contribution?: SchemaContribution): PreparedSchema {
|
|
267
|
+
const sources = Object.entries(contribution?.sources ?? {}).map(
|
|
268
|
+
([type, definition]) => prepareType("Source", plugin, type, definition),
|
|
269
|
+
)
|
|
270
|
+
const blocks = Object.entries(contribution?.blocks ?? {}).map(
|
|
271
|
+
([type, definition]) => prepareType("Block", plugin, type, definition),
|
|
272
|
+
)
|
|
273
|
+
const props = Object.entries(contribution?.props ?? {}).map(
|
|
274
|
+
([name, schema]) => prepareProp(plugin, name, schema),
|
|
275
|
+
)
|
|
276
|
+
const queries = Object.entries(contribution?.query ?? {}).map(
|
|
277
|
+
([name, definition]) => prepareQuery(plugin, name, definition),
|
|
278
|
+
)
|
|
279
|
+
const actions = Object.entries(contribution?.actions ?? {}).map(
|
|
280
|
+
([name, action]) => prepareAction(plugin, name, action),
|
|
281
|
+
)
|
|
282
|
+
for (const prop of props) {
|
|
283
|
+
const existing = this.propContributions.get(prop.name)
|
|
284
|
+
if (existing) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`Data prop ${prop.name} is already owned by Plugin ${existing.plugin}.`,
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
for (const query of queries) {
|
|
291
|
+
if (this.queryContributions.has(query.id)) {
|
|
292
|
+
throw new Error(`Duplicate Plugin query: ${query.id}`)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return Object.freeze({
|
|
296
|
+
sources: Object.freeze(sources),
|
|
297
|
+
blocks: Object.freeze(blocks),
|
|
298
|
+
props: Object.freeze(props),
|
|
299
|
+
queries: Object.freeze(queries),
|
|
300
|
+
actions: Object.freeze(actions),
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
register(prepared: PreparedSchema) {
|
|
305
|
+
this.registerTypes(
|
|
306
|
+
prepared.sources,
|
|
307
|
+
this.sourceContributions,
|
|
308
|
+
this.sourceDescriptors,
|
|
309
|
+
)
|
|
310
|
+
this.registerTypes(
|
|
311
|
+
prepared.blocks,
|
|
312
|
+
this.blockContributions,
|
|
313
|
+
this.blockDescriptors,
|
|
314
|
+
)
|
|
315
|
+
for (const contribution of prepared.props) {
|
|
316
|
+
this.propContributions.set(contribution.name, contribution)
|
|
317
|
+
}
|
|
318
|
+
for (const query of prepared.queries) {
|
|
319
|
+
this.queryContributions.set(query.id, query)
|
|
320
|
+
this.queryReferences.set(query.definition, query)
|
|
321
|
+
this.queryDescriptions.set(
|
|
322
|
+
query.id,
|
|
323
|
+
Object.freeze({
|
|
324
|
+
id: query.id,
|
|
325
|
+
plugin: query.plugin,
|
|
326
|
+
...(query.definition.input
|
|
327
|
+
? {
|
|
328
|
+
input: query.definition.input,
|
|
329
|
+
inputJsonSchema: query.input,
|
|
330
|
+
}
|
|
331
|
+
: {}),
|
|
332
|
+
output: query.definition.output,
|
|
333
|
+
outputJsonSchema: query.output,
|
|
334
|
+
}),
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private registerTypes(
|
|
340
|
+
contributions: readonly TypeContribution[],
|
|
341
|
+
byType: Map<string, TypeContribution[]>,
|
|
342
|
+
descriptors: Map<string, NodeType>,
|
|
343
|
+
) {
|
|
344
|
+
for (const contribution of contributions) {
|
|
345
|
+
const definitions = byType.get(contribution.type)
|
|
346
|
+
if (definitions) definitions.push(contribution)
|
|
347
|
+
else byType.set(contribution.type, [contribution])
|
|
348
|
+
descriptors.delete(contribution.type)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
sourceType(sourceOrType: SourceNode | string) {
|
|
353
|
+
const type =
|
|
354
|
+
typeof sourceOrType === "string" ? sourceOrType : sourceOrType.type
|
|
355
|
+
return this.typeDescriptor(
|
|
356
|
+
type,
|
|
357
|
+
this.sourceContributions,
|
|
358
|
+
this.sourceDescriptors,
|
|
359
|
+
this.observedSourceDescriptors,
|
|
360
|
+
)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
sourceTypes() {
|
|
364
|
+
return this.typeDescriptors(
|
|
365
|
+
this.sourceContributions,
|
|
366
|
+
this.observedSourceDescriptors,
|
|
367
|
+
(type) => this.sourceType(type),
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
blockType(blockOrType: SourceNode | string) {
|
|
372
|
+
const type =
|
|
373
|
+
typeof blockOrType === "string" ? blockOrType : blockOrType.type
|
|
374
|
+
return this.typeDescriptor(
|
|
375
|
+
type,
|
|
376
|
+
this.blockContributions,
|
|
377
|
+
this.blockDescriptors,
|
|
378
|
+
this.observedBlockDescriptors,
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
blockTypes() {
|
|
383
|
+
return this.typeDescriptors(
|
|
384
|
+
this.blockContributions,
|
|
385
|
+
this.observedBlockDescriptors,
|
|
386
|
+
(type) => this.blockType(type),
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private typeDescriptor(
|
|
391
|
+
type: string,
|
|
392
|
+
contributionsByType: Map<string, TypeContribution[]>,
|
|
393
|
+
descriptors: Map<string, NodeType>,
|
|
394
|
+
observed: Map<string, NodeType>,
|
|
395
|
+
) {
|
|
396
|
+
const contributions = contributionsByType.get(type)
|
|
397
|
+
if (!contributions?.length) return this.observedType(type, observed)
|
|
398
|
+
let descriptor = descriptors.get(type)
|
|
399
|
+
if (descriptor) return descriptor
|
|
400
|
+
let label: string | undefined
|
|
401
|
+
let description: string | undefined
|
|
402
|
+
let icon: string | undefined
|
|
403
|
+
let color: string | undefined
|
|
404
|
+
for (const { definition } of contributions) {
|
|
405
|
+
label = definition.label ?? label
|
|
406
|
+
description = definition.description ?? description
|
|
407
|
+
icon = definition.icon ?? icon
|
|
408
|
+
color = definition.color ?? color
|
|
409
|
+
}
|
|
410
|
+
descriptor = Object.freeze({
|
|
411
|
+
type,
|
|
412
|
+
label: label ?? titleCase(type),
|
|
413
|
+
...(description ? { description } : {}),
|
|
414
|
+
icon: icon ?? "file-text",
|
|
415
|
+
color: color ?? "slate",
|
|
416
|
+
origin: "defined",
|
|
417
|
+
plugins: Object.freeze([
|
|
418
|
+
...new Set(contributions.map((contribution) => contribution.plugin)),
|
|
419
|
+
]),
|
|
420
|
+
})
|
|
421
|
+
descriptors.set(type, descriptor)
|
|
422
|
+
return descriptor
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private typeDescriptors(
|
|
426
|
+
contributions: Map<string, TypeContribution[]>,
|
|
427
|
+
observed: Map<string, NodeType>,
|
|
428
|
+
resolve: (type: string) => NodeType,
|
|
429
|
+
) {
|
|
430
|
+
const types = new Set([...contributions.keys(), ...observed.keys()])
|
|
431
|
+
return Object.freeze([...types].sort(compareText).map(resolve))
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
sourceSchemas(sourceOrType: SourceNode | string) {
|
|
435
|
+
const type =
|
|
436
|
+
typeof sourceOrType === "string" ? sourceOrType : sourceOrType.type
|
|
437
|
+
return this.schemas(type, this.sourceContributions)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
blockSchemas(blockOrType: SourceNode | string) {
|
|
441
|
+
const type =
|
|
442
|
+
typeof blockOrType === "string" ? blockOrType : blockOrType.type
|
|
443
|
+
return this.schemas(type, this.blockContributions)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
private schemas(
|
|
447
|
+
type: string,
|
|
448
|
+
contributionsByType: Map<string, TypeContribution[]>,
|
|
449
|
+
) {
|
|
450
|
+
return Object.freeze(
|
|
451
|
+
(contributionsByType.get(type) ?? []).flatMap((contribution) =>
|
|
452
|
+
contribution.definition.schema
|
|
453
|
+
? [
|
|
454
|
+
Object.freeze({
|
|
455
|
+
plugin: contribution.plugin,
|
|
456
|
+
schema: contribution.definition.schema,
|
|
457
|
+
inputJsonSchema: contribution.inputJsonSchema!,
|
|
458
|
+
outputJsonSchema: contribution.outputJsonSchema!,
|
|
459
|
+
}) satisfies NodeSchema,
|
|
460
|
+
]
|
|
461
|
+
: [],
|
|
462
|
+
),
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
prop(name: string) {
|
|
467
|
+
return this.propContributions.get(name)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
props(node?: SourceNode) {
|
|
471
|
+
const definitions = node
|
|
472
|
+
? Object.keys(node.data).flatMap((key) => {
|
|
473
|
+
const prop = this.propContributions.get(key)
|
|
474
|
+
return prop ? [prop] : []
|
|
475
|
+
})
|
|
476
|
+
: [...this.propContributions.values()]
|
|
477
|
+
return Object.freeze(
|
|
478
|
+
definitions.sort((left, right) => compareText(left.name, right.name)),
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
has(node: SourceNode, prop: string) {
|
|
483
|
+
return this.propContributions.has(prop) && Object.hasOwn(node.data, prop)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
value(node: SourceNode, prop: string) {
|
|
487
|
+
return this.has(node, prop) ? node.data[prop] : undefined
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
registeredQuery(reference: string | QueryContribution) {
|
|
491
|
+
return typeof reference === "string"
|
|
492
|
+
? this.queryContributions.get(reference)
|
|
493
|
+
: this.queryReferences.get(reference)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
queries() {
|
|
497
|
+
return Object.freeze(
|
|
498
|
+
[...this.queryContributions.values()].sort((left, right) =>
|
|
499
|
+
compareText(left.id, right.id),
|
|
500
|
+
),
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
querySchema(id: string) {
|
|
505
|
+
return this.queryDescriptions.get(id)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
querySchemas() {
|
|
509
|
+
return Object.freeze(
|
|
510
|
+
[...this.queryDescriptions.values()].sort((left, right) =>
|
|
511
|
+
compareText(left.id, right.id),
|
|
512
|
+
),
|
|
513
|
+
)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
sourceTypeContributions(type?: string) {
|
|
517
|
+
return type
|
|
518
|
+
? Object.freeze([...(this.sourceContributions.get(type) ?? [])])
|
|
519
|
+
: Object.freeze([...this.sourceContributions.values()].flat())
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
blockTypeContributions(type?: string) {
|
|
523
|
+
return type
|
|
524
|
+
? Object.freeze([...(this.blockContributions.get(type) ?? [])])
|
|
525
|
+
: Object.freeze([...this.blockContributions.values()].flat())
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
sourceDataSchemas(type?: string) {
|
|
529
|
+
return Object.freeze(
|
|
530
|
+
this.sourceTypeContributions(type).flatMap((entry) =>
|
|
531
|
+
entry.outputJsonSchema ? [entry.outputJsonSchema] : [],
|
|
532
|
+
),
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
blockDataSchemas(type?: string) {
|
|
537
|
+
return Object.freeze(
|
|
538
|
+
this.blockTypeContributions(type).flatMap((entry) =>
|
|
539
|
+
entry.outputJsonSchema ? [entry.outputJsonSchema] : [],
|
|
540
|
+
),
|
|
541
|
+
)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
propDataSchema(name: string) {
|
|
545
|
+
return this.propContributions.get(name)?.outputJsonSchema
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
index(source: Source, onError?: (error: unknown) => boolean) {
|
|
549
|
+
this.sourceType(source)
|
|
550
|
+
for (const { plugin, definition } of this.sourceContributions.get(
|
|
551
|
+
source.type,
|
|
552
|
+
) ?? []) {
|
|
553
|
+
if (!definition.schema) continue
|
|
554
|
+
report(
|
|
555
|
+
validate(
|
|
556
|
+
source,
|
|
557
|
+
source,
|
|
558
|
+
plugin,
|
|
559
|
+
`${source.type} Source`,
|
|
560
|
+
definition.schema,
|
|
561
|
+
source.data,
|
|
562
|
+
),
|
|
563
|
+
onError,
|
|
564
|
+
)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
for (const block of source.blocks) {
|
|
568
|
+
this.blockType(block)
|
|
569
|
+
for (const { plugin, definition } of this.blockContributions.get(
|
|
570
|
+
block.type,
|
|
571
|
+
) ?? []) {
|
|
572
|
+
if (!definition.schema) continue
|
|
573
|
+
report(
|
|
574
|
+
validate(
|
|
575
|
+
source,
|
|
576
|
+
block,
|
|
577
|
+
plugin,
|
|
578
|
+
`${block.type} Block`,
|
|
579
|
+
definition.schema,
|
|
580
|
+
block.data,
|
|
581
|
+
),
|
|
582
|
+
onError,
|
|
583
|
+
)
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
if (!this.propContributions.size) return
|
|
588
|
+
for (const node of [source, ...source.blocks]) {
|
|
589
|
+
for (const key of Object.keys(node.data)) {
|
|
590
|
+
const prop = this.propContributions.get(key)
|
|
591
|
+
if (!prop) continue
|
|
592
|
+
report(
|
|
593
|
+
validate(
|
|
594
|
+
source,
|
|
595
|
+
node,
|
|
596
|
+
prop.plugin,
|
|
597
|
+
`${key} prop`,
|
|
598
|
+
prop.schema,
|
|
599
|
+
node.data[key],
|
|
600
|
+
),
|
|
601
|
+
onError,
|
|
602
|
+
)
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
private observedType(type: string, observed: Map<string, NodeType>) {
|
|
608
|
+
let descriptor = observed.get(type)
|
|
609
|
+
if (!descriptor) {
|
|
610
|
+
descriptor = Object.freeze({
|
|
611
|
+
type,
|
|
612
|
+
label: titleCase(type),
|
|
613
|
+
icon: "file-text",
|
|
614
|
+
color: "slate",
|
|
615
|
+
origin: "observed",
|
|
616
|
+
plugins: Object.freeze([]),
|
|
617
|
+
})
|
|
618
|
+
observed.set(type, descriptor)
|
|
619
|
+
}
|
|
620
|
+
return descriptor
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** @internal Expose only stable Schema queries outside the Plugin host. */
|
|
625
|
+
export function applicationSchemaView(
|
|
626
|
+
registry: SchemaRegistry,
|
|
627
|
+
): ApplicationSchema {
|
|
628
|
+
return Object.freeze({
|
|
629
|
+
sourceType: registry.sourceType.bind(registry),
|
|
630
|
+
sourceTypes: registry.sourceTypes.bind(registry),
|
|
631
|
+
blockType: registry.blockType.bind(registry),
|
|
632
|
+
blockTypes: registry.blockTypes.bind(registry),
|
|
633
|
+
sourceSchemas: registry.sourceSchemas.bind(registry),
|
|
634
|
+
blockSchemas: registry.blockSchemas.bind(registry),
|
|
635
|
+
query: registry.querySchema.bind(registry),
|
|
636
|
+
queries: registry.querySchemas.bind(registry),
|
|
637
|
+
prop: registry.prop.bind(registry),
|
|
638
|
+
props: registry.props.bind(registry),
|
|
639
|
+
has: registry.has.bind(registry),
|
|
640
|
+
value: registry.value.bind(registry),
|
|
641
|
+
})
|
|
642
|
+
}
|