@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/view/model.ts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { ActionAffordance } from "../actions/actions.ts"
|
|
2
|
+
import type { Actor, Principal, UserActor } from "../authority/model.ts"
|
|
3
|
+
import type { ViewQuery } from "../query/model.ts"
|
|
4
|
+
import type { BlockType, SourceType } from "../schema/model.ts"
|
|
5
|
+
import type { MarkdownList, MarkdownTask } from "../source/index.ts"
|
|
6
|
+
|
|
7
|
+
export type ViewIdentity =
|
|
8
|
+
| Readonly<{ type: "anonymous" }>
|
|
9
|
+
| Readonly<{ type: "authenticated"; subject: UserActor }>
|
|
10
|
+
| Readonly<{ type: "inspection"; subject: Actor }>
|
|
11
|
+
|
|
12
|
+
type SourceViewMeta = Readonly<{
|
|
13
|
+
title: string
|
|
14
|
+
description?: string
|
|
15
|
+
image?: string
|
|
16
|
+
}>
|
|
17
|
+
|
|
18
|
+
type BlockViewMeta = Readonly<{
|
|
19
|
+
title?: string
|
|
20
|
+
description?: string
|
|
21
|
+
image?: string
|
|
22
|
+
}>
|
|
23
|
+
|
|
24
|
+
export type SourceView = Readonly<{
|
|
25
|
+
id: string
|
|
26
|
+
type: string
|
|
27
|
+
href: string
|
|
28
|
+
sourceType: SourceType
|
|
29
|
+
qualifiers: readonly string[]
|
|
30
|
+
data: Readonly<Record<string, unknown>>
|
|
31
|
+
body: string
|
|
32
|
+
blocks: readonly BlockView[]
|
|
33
|
+
meta: SourceViewMeta
|
|
34
|
+
}>
|
|
35
|
+
|
|
36
|
+
export type BlockView = Readonly<{
|
|
37
|
+
id: string
|
|
38
|
+
type: string
|
|
39
|
+
href: string
|
|
40
|
+
blockType: BlockType
|
|
41
|
+
data: Readonly<Record<string, unknown>>
|
|
42
|
+
body: string
|
|
43
|
+
lists: readonly MarkdownList[]
|
|
44
|
+
tasks: readonly MarkdownTask[]
|
|
45
|
+
meta: BlockViewMeta
|
|
46
|
+
}>
|
|
47
|
+
|
|
48
|
+
export type SourceSummary = Readonly<{
|
|
49
|
+
id: string
|
|
50
|
+
type: string
|
|
51
|
+
href: string
|
|
52
|
+
sourceType: SourceType
|
|
53
|
+
qualifiers: readonly string[]
|
|
54
|
+
data: Readonly<Record<string, unknown>>
|
|
55
|
+
meta: SourceView["meta"]
|
|
56
|
+
}>
|
|
57
|
+
|
|
58
|
+
export type BlockSummary = BlockView &
|
|
59
|
+
Readonly<{
|
|
60
|
+
source: SourceSummary
|
|
61
|
+
}>
|
|
62
|
+
|
|
63
|
+
export type SourceRelation = Readonly<{
|
|
64
|
+
rel: string
|
|
65
|
+
qualifiers: readonly string[]
|
|
66
|
+
type:
|
|
67
|
+
| "source"
|
|
68
|
+
| "asset"
|
|
69
|
+
| "external"
|
|
70
|
+
| "fragment"
|
|
71
|
+
| "actor-relative"
|
|
72
|
+
| "unresolved"
|
|
73
|
+
label?: string
|
|
74
|
+
target?: Readonly<{ type: "source"; id: string }>
|
|
75
|
+
href: string
|
|
76
|
+
fragment?: string
|
|
77
|
+
}>
|
|
78
|
+
|
|
79
|
+
export type SourceDescription = Readonly<{
|
|
80
|
+
source: SourceView
|
|
81
|
+
relations: readonly SourceRelation[]
|
|
82
|
+
actions: readonly ActionAffordance[]
|
|
83
|
+
}>
|
|
84
|
+
|
|
85
|
+
export type View = Readonly<{
|
|
86
|
+
identity: ViewIdentity
|
|
87
|
+
principal: Principal
|
|
88
|
+
query: ViewQuery
|
|
89
|
+
readSource(id: string): SourceView | undefined
|
|
90
|
+
describeSource(id: string): Promise<SourceDescription | undefined>
|
|
91
|
+
}>
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { publicPath } from "../server/router.ts"
|
|
2
|
+
import {
|
|
3
|
+
deriveBlockMeta,
|
|
4
|
+
deriveSourceMeta,
|
|
5
|
+
type Source,
|
|
6
|
+
type SourceBlock,
|
|
7
|
+
} from "../source/index.ts"
|
|
8
|
+
import type { ApplicationSchema, SourceType } from "../schema/model.ts"
|
|
9
|
+
import type {
|
|
10
|
+
BlockSummary,
|
|
11
|
+
BlockView,
|
|
12
|
+
SourceSummary,
|
|
13
|
+
SourceView,
|
|
14
|
+
} from "./model.ts"
|
|
15
|
+
|
|
16
|
+
const sourceViews = new WeakMap<Source, WeakMap<SourceType, SourceView>>()
|
|
17
|
+
const sourceSummaries = new WeakMap<
|
|
18
|
+
Source,
|
|
19
|
+
WeakMap<SourceType, SourceSummary>
|
|
20
|
+
>()
|
|
21
|
+
|
|
22
|
+
function immutableValue(
|
|
23
|
+
value: unknown,
|
|
24
|
+
seen = new WeakMap<object, unknown>(),
|
|
25
|
+
): unknown {
|
|
26
|
+
if (Array.isArray(value)) {
|
|
27
|
+
const existing = seen.get(value)
|
|
28
|
+
if (existing) return existing
|
|
29
|
+
const projected: unknown[] = []
|
|
30
|
+
seen.set(value, projected)
|
|
31
|
+
for (const item of value) projected.push(immutableValue(item, seen))
|
|
32
|
+
return Object.freeze(projected)
|
|
33
|
+
}
|
|
34
|
+
if (value == null || typeof value !== "object") return value
|
|
35
|
+
|
|
36
|
+
const existing = seen.get(value)
|
|
37
|
+
if (existing) return existing
|
|
38
|
+
const projected = Object.create(null) as Record<string, unknown>
|
|
39
|
+
seen.set(value, projected)
|
|
40
|
+
for (const [key, item] of Object.entries(value)) {
|
|
41
|
+
projected[key] = immutableValue(item, seen)
|
|
42
|
+
}
|
|
43
|
+
return Object.freeze(projected)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function projectionCache<T>(
|
|
47
|
+
cache: WeakMap<Source, WeakMap<SourceType, T>>,
|
|
48
|
+
source: Source,
|
|
49
|
+
) {
|
|
50
|
+
let projections = cache.get(source)
|
|
51
|
+
if (!projections) {
|
|
52
|
+
projections = new WeakMap()
|
|
53
|
+
cache.set(source, projections)
|
|
54
|
+
}
|
|
55
|
+
return projections
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function projectMeta(derived: {
|
|
59
|
+
title: string
|
|
60
|
+
description: string
|
|
61
|
+
image: string
|
|
62
|
+
}): SourceView["meta"] {
|
|
63
|
+
return Object.freeze({
|
|
64
|
+
title: derived.title,
|
|
65
|
+
...(derived.description ? { description: derived.description } : {}),
|
|
66
|
+
...(derived.image ? { image: derived.image } : {}),
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function projectBlockMeta(derived: {
|
|
71
|
+
title: string
|
|
72
|
+
description: string
|
|
73
|
+
image: string
|
|
74
|
+
}): BlockView["meta"] {
|
|
75
|
+
return Object.freeze({
|
|
76
|
+
...(derived.title ? { title: derived.title } : {}),
|
|
77
|
+
...(derived.description ? { description: derived.description } : {}),
|
|
78
|
+
...(derived.image ? { image: derived.image } : {}),
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function projectBlock(
|
|
83
|
+
source: Source,
|
|
84
|
+
block: SourceBlock,
|
|
85
|
+
schema: ApplicationSchema,
|
|
86
|
+
): BlockView {
|
|
87
|
+
let meta: BlockView["meta"] | undefined
|
|
88
|
+
return Object.freeze({
|
|
89
|
+
id: block.id,
|
|
90
|
+
type: block.type,
|
|
91
|
+
href: `${publicPath(source)}#${encodeURIComponent(block.id)}`,
|
|
92
|
+
blockType: schema.blockType(block),
|
|
93
|
+
data: immutableValue(block.data) as Readonly<Record<string, unknown>>,
|
|
94
|
+
body: block.body,
|
|
95
|
+
/** Preserve lazy indexing while exposing only immutable list data. */
|
|
96
|
+
get lists() {
|
|
97
|
+
return block.lists
|
|
98
|
+
},
|
|
99
|
+
/** Tasks and lists share the canonical Block's lazy structural parse. */
|
|
100
|
+
get tasks() {
|
|
101
|
+
return block.tasks
|
|
102
|
+
},
|
|
103
|
+
/** Derive Block metadata only for consumers that request it. */
|
|
104
|
+
get meta() {
|
|
105
|
+
return (meta ??= projectBlockMeta(deriveBlockMeta(block, source)))
|
|
106
|
+
},
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** @internal Project one canonical Source for query and View consumers. */
|
|
111
|
+
export function projectSourceSummary(
|
|
112
|
+
source: Source,
|
|
113
|
+
schema: ApplicationSchema,
|
|
114
|
+
) {
|
|
115
|
+
const sourceType = schema.sourceType(source)
|
|
116
|
+
const cache = projectionCache(sourceSummaries, source)
|
|
117
|
+
const cached = cache.get(sourceType)
|
|
118
|
+
if (cached) return cached
|
|
119
|
+
const summary: SourceSummary = Object.freeze({
|
|
120
|
+
id: source.id,
|
|
121
|
+
type: source.type,
|
|
122
|
+
href: publicPath(source),
|
|
123
|
+
sourceType,
|
|
124
|
+
qualifiers: Object.freeze([...source.qualifiers]),
|
|
125
|
+
data: immutableValue(source.data) as Readonly<Record<string, unknown>>,
|
|
126
|
+
meta: projectMeta(deriveSourceMeta(source)),
|
|
127
|
+
})
|
|
128
|
+
cache.set(sourceType, summary)
|
|
129
|
+
return summary
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** @internal Project one canonical Block with its containing Source context. */
|
|
133
|
+
export function projectBlockSummary(
|
|
134
|
+
source: Source,
|
|
135
|
+
block: SourceBlock,
|
|
136
|
+
schema: ApplicationSchema,
|
|
137
|
+
): BlockSummary {
|
|
138
|
+
return Object.freeze({
|
|
139
|
+
...projectBlock(source, block, schema),
|
|
140
|
+
source: projectSourceSummary(source, schema),
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** @internal Project one complete canonical Source for a View. */
|
|
145
|
+
export function projectSource(source: Source, schema: ApplicationSchema) {
|
|
146
|
+
const sourceType = schema.sourceType(source)
|
|
147
|
+
const cache = projectionCache(sourceViews, source)
|
|
148
|
+
const cached = cache.get(sourceType)
|
|
149
|
+
if (cached) return cached
|
|
150
|
+
|
|
151
|
+
const summary = projectSourceSummary(source, schema)
|
|
152
|
+
const projected: SourceView = Object.freeze({
|
|
153
|
+
...summary,
|
|
154
|
+
data: immutableValue(source.data) as Readonly<Record<string, unknown>>,
|
|
155
|
+
body: source.body,
|
|
156
|
+
blocks: Object.freeze(
|
|
157
|
+
source.blocks.map((block) => projectBlock(source, block, schema)),
|
|
158
|
+
),
|
|
159
|
+
})
|
|
160
|
+
cache.set(sourceType, projected)
|
|
161
|
+
return projected
|
|
162
|
+
}
|
package/view/session.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { Engine } from "../host/engine.ts"
|
|
2
|
+
import { canonicalPath } from "../canonical.ts"
|
|
3
|
+
import type { Source } from "../source/index.ts"
|
|
4
|
+
import type { Actor, CredentialSession, Principal } from "../authority/model.ts"
|
|
5
|
+
import { principalFor } from "../authority/model.ts"
|
|
6
|
+
import type { View, ViewIdentity } from "./model.ts"
|
|
7
|
+
import type { ViewQuery } from "../query/model.ts"
|
|
8
|
+
import { projectSource } from "./projection.ts"
|
|
9
|
+
|
|
10
|
+
function identityFor(session: CredentialSession): ViewIdentity {
|
|
11
|
+
return session.type === "authenticated"
|
|
12
|
+
? Object.freeze({ type: "authenticated", subject: session.actor })
|
|
13
|
+
: Object.freeze({ type: "anonymous" })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function within(id: string, root: string) {
|
|
17
|
+
return root === "/" || id === root || id.startsWith(`${root}/`)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function actorRelativeSource(
|
|
21
|
+
engine: Engine,
|
|
22
|
+
principal: Principal,
|
|
23
|
+
href: string,
|
|
24
|
+
) {
|
|
25
|
+
if (principal.type !== "actor" || !href.startsWith("~/")) return
|
|
26
|
+
const pathname = href.slice(2).split(/[?#]/, 1)[0]
|
|
27
|
+
if (!pathname) return
|
|
28
|
+
const candidate = engine.environment.path.join(principal.actor.home, pathname)
|
|
29
|
+
try {
|
|
30
|
+
canonicalPath(candidate)
|
|
31
|
+
} catch {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
if (!within(candidate, principal.actor.home)) return
|
|
35
|
+
return (
|
|
36
|
+
engine.repo.get(candidate) ??
|
|
37
|
+
engine.repo.atPath(candidate) ??
|
|
38
|
+
engine.resolve(candidate)
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class EngineView implements View {
|
|
43
|
+
readonly #engine: Engine
|
|
44
|
+
readonly identity: ViewIdentity
|
|
45
|
+
readonly principal: Principal
|
|
46
|
+
readonly query: ViewQuery
|
|
47
|
+
|
|
48
|
+
constructor(engine: Engine, identity: ViewIdentity, principal: Principal) {
|
|
49
|
+
this.#engine = engine
|
|
50
|
+
this.identity = identity
|
|
51
|
+
this.principal = principal
|
|
52
|
+
this.query = engine.queries.viewQuery(this)
|
|
53
|
+
Object.freeze(this)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
readSource(id: string) {
|
|
57
|
+
const source = this.readableSource(id)
|
|
58
|
+
if (!source) return
|
|
59
|
+
return projectSource(source, this.#engine.schema)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async describeSource(id: string) {
|
|
63
|
+
const source = this.readableSource(id)
|
|
64
|
+
if (!source) return
|
|
65
|
+
const actions = await this.#engine.actionAffordances(this.principal, source)
|
|
66
|
+
return Object.freeze({
|
|
67
|
+
source: projectSource(source, this.#engine.schema),
|
|
68
|
+
relations: Object.freeze(this.relationsFrom(source)),
|
|
69
|
+
actions,
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private readableSource(id: string) {
|
|
74
|
+
const source = this.#engine.repo.get(id)
|
|
75
|
+
return source &&
|
|
76
|
+
this.#engine.readAccess(this.principal, source) !== "denied"
|
|
77
|
+
? source
|
|
78
|
+
: undefined
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private relationsFrom(source: Source) {
|
|
82
|
+
return this.#engine.graph.linksFrom(source.path).map((edge) => {
|
|
83
|
+
const common = {
|
|
84
|
+
rel: edge.directive?.name ?? "link",
|
|
85
|
+
qualifiers: Object.freeze([...(edge.directive?.qualifiers ?? [])]),
|
|
86
|
+
href: edge.href,
|
|
87
|
+
...(edge.label ? { label: edge.label } : {}),
|
|
88
|
+
...(edge.fragment ? { fragment: edge.fragment } : {}),
|
|
89
|
+
}
|
|
90
|
+
if (edge.type === "source" && edge.to) {
|
|
91
|
+
const target = this.#engine.repo.atPath(edge.to)
|
|
92
|
+
if (
|
|
93
|
+
target &&
|
|
94
|
+
this.#engine.readAccess(this.principal, target) !== "denied"
|
|
95
|
+
) {
|
|
96
|
+
return Object.freeze({
|
|
97
|
+
...common,
|
|
98
|
+
type: "source" as const,
|
|
99
|
+
target: Object.freeze({ type: "source" as const, id: target.id }),
|
|
100
|
+
})
|
|
101
|
+
}
|
|
102
|
+
return Object.freeze({
|
|
103
|
+
...common,
|
|
104
|
+
type: "unresolved" as const,
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
if (edge.type === "actor-relative") {
|
|
108
|
+
const target = actorRelativeSource(
|
|
109
|
+
this.#engine,
|
|
110
|
+
this.principal,
|
|
111
|
+
edge.href,
|
|
112
|
+
)
|
|
113
|
+
if (
|
|
114
|
+
target &&
|
|
115
|
+
this.#engine.readAccess(this.principal, target) !== "denied"
|
|
116
|
+
) {
|
|
117
|
+
return Object.freeze({
|
|
118
|
+
...common,
|
|
119
|
+
type: "source" as const,
|
|
120
|
+
target: Object.freeze({ type: "source" as const, id: target.id }),
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return Object.freeze({
|
|
125
|
+
...common,
|
|
126
|
+
type: edge.type,
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** @internal Construct only from a CredentialSession admitted by a host. */
|
|
133
|
+
export function createView(engine: Engine, session: CredentialSession): View {
|
|
134
|
+
return new EngineView(engine, identityFor(session), principalFor(session))
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** @internal Create a trusted local inspection View without a credential session. */
|
|
138
|
+
export function createInspectionView(engine: Engine, actor?: Actor): View {
|
|
139
|
+
return new EngineView(
|
|
140
|
+
engine,
|
|
141
|
+
actor
|
|
142
|
+
? Object.freeze({ type: "inspection", subject: actor })
|
|
143
|
+
: Object.freeze({ type: "anonymous" }),
|
|
144
|
+
actor ? Object.freeze({ type: "actor", actor }) : { type: "anonymous" },
|
|
145
|
+
)
|
|
146
|
+
}
|