Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 35 additions & 9 deletions architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -3714,7 +3714,7 @@ assembled the run. Above them sits the engine's own, and a name in it means the
same thing in every execution: whichever host built it, whichever package
registered what, and whatever the repository holds.

One component is in it. `<Syntax />` describes the vocabulary of the site it is
`<Syntax />` and `<Evaluate>` are in it. `<Syntax />` describes the vocabulary of the site it is
written at, and a description of a run's vocabulary that anything in the run
could answer for is a description of nothing — the value of the answer is
exactly that nobody but core produced it.
Expand Down Expand Up @@ -3743,14 +3743,40 @@ and verified where the component is invoked, and core's own copy is what runs.
Every other name in the execution stays the ordinary open import it has always
been.

**Protection is about the answer, not about power.** A protected implementation
is handed the lexical syntax reference for its site and nothing else: no
component definitions, no import witness, no invocation capability, no policy
table, no provider and no registration handle. The body itself is kept in a table
private to the copy of core that built the implementation and reached only by
canonical expansion, so an implementation another loaded copy created — which is
an ordinary arrangement, because a component can be loaded from disk beside its
own copy — has no body here and no answer to give.
**Protection is about the answer, not about power.** Canonical expansion hands a
protected body its lexical syntax reference and the internal operations its
site owns. `<Evaluate>` receives the captured evaluation profile, its paired
content projection and an operation that derives a child protected route for
exact admitted definitions. None is published through a context, component API,
import answer or public host input.

Protected lookup uses exact function identity. The execution's route projects
an already routed source onto a wrapper without exposing the body; an
unprotected source projects nothing. Profile sealing projects the first wrapper
onto the full execution route. `<Evaluate>` derives a fresh child route seeded
only with its selected sealed definitions, and passes that route and its narrowed
lexical `SyntaxReference` internally to generated expansion. Generated import
projects its form/result wrapper only within the child route. Canonical dispatch
issues the invocation in the routed body's original domain and applies generated
form checks and read-result collection around either kind of body. Authored
imports still require their own settled selection frame; a routed function
cannot repair a missing or multiply selected domain. Paired producers retain
their lexical site route and operational authority.

A trusted provider may therefore delegate and claim canonical self-closing
`<Syntax>` as a component answer. Its bare form reports the admitted vocabulary;
its named form renders the enclosing authoring documentation with narrowed
availability. Verified protected documentation provenance survives sealing even
when the delegating provider has a different origin. That provenance is
descriptive data and never a callable lookup key.

Routes, projection edges, bodies, domains, callable sets and site operations
belong to one live execution. Child closure and execution teardown invalidate
retained operations. A same-name function, structural clone, independent wrapper,
unadmitted definition or implementation another loaded copy built gains no
route. Durable records keep their existing structural identities, forms, source,
profile inputs and results; they contain no callable authority. Continuation
rebuilds routes from the resuming execution's own verified answers.

**The named form is a second question.** Bare `<Syntax />` answers *what may I
write here*. `<Syntax names={["Elicit"]} />` answers *how do I use this one*, and
Expand Down
12 changes: 8 additions & 4 deletions packages/core/src/components/Evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import type { Operation } from "effection";
import { getExpansion } from "../expansion.ts";
import { NO_PROFILE, REVOKED } from "../evaluation-profile.ts";
import type { CapturedEntry, CapturedProfile } from "../evaluation-profile.ts";
import { evaluateGeneratedXmd } from "../generated-xmd.ts";
import { evaluateProtectedGeneratedXmd } from "../generated-xmd.ts";
import type {
GeneratedEffectClass,
GeneratedMutation,
Expand Down Expand Up @@ -224,8 +224,8 @@ function evaluate(claim: IdentityClaimant): ProtectedBody {
// producer is told about is the vocabulary the fragment is admitted for —
// and reported as availability against the enclosing reference, which keeps
// the authoring documentation the site already had.
const narrowed = narrow(site.syntax, entries);
const source = stated === undefined ? yield* project(site, narrowed) : stated;
const narrowedSyntax = narrow(site.syntax, entries);
const source = stated === undefined ? yield* project(site, narrowedSyntax) : stated;

// Read after the producer has rendered, and exactly once per occurrence: a
// producer may itself commit mutations, and the basis this admission is
Expand Down Expand Up @@ -254,9 +254,13 @@ function evaluate(claim: IdentityClaimant): ProtectedBody {
// fragment produced inside another fragment's producer leaves the outer one
// where it was, and a failed one leaves nothing behind.
const leave = yield* profile.enterFragment();
const narrowedBodies = site.narrowProtectedBodies(
entries.admitted.map((entry) => entry.definition.fn),
);
try {
return answer(yield* evaluateGeneratedXmd(request));
return answer(yield* evaluateProtectedGeneratedXmd(request, narrowedBodies, narrowedSyntax));
} finally {
narrowedBodies?.close();
leave();
}
};
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/components/import-authority.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
*/

import type { ComponentDefinition, FunctionComponentDefinition, SourcePosition } from "../types.ts";
import type { Operation } from "effection";
import type { ComponentInvocation } from "../invocation-identity.ts";
import type {
FormSelections,
InvocationIdentities,
Expand Down Expand Up @@ -147,6 +149,12 @@ export interface ExpansionAuthority {
* kept past this execution's teardown reaches a table that is gone.
*/
readonly protectedBodies?: ProtectedBodies;
/** The generated import's form check and result collection, around either body kind. */
readonly invoke?: (
fn: unknown,
invocation: ComponentInvocation,
body: Operation<unknown>,
) => Operation<unknown>;
}

/** Why an answer is not the one canonical execution produced for this name. */
Expand Down
23 changes: 17 additions & 6 deletions packages/core/src/evaluation-profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import type {
FragmentFileAccess,
} from "./fragment-capabilities.ts";
import { isFormDispatcher } from "./invocation-identity.ts";
import type { ComponentInvocation } from "./invocation-identity.ts";
import type { ComponentInvocation, ProtectedBodies } from "./invocation-identity.ts";
import type { FetchRequest } from "./fetch-request.ts";
import { normalizeFetchRequest, requestRecord } from "./fetch-request.ts";
import { CORE_REVISION } from "./generated-xmd.ts";
Expand Down Expand Up @@ -411,6 +411,8 @@ export type ResolvedAnswers = ReadonlyMap<string, ResolvedAnswer>;
* own operation or a provider's answer, and the two are different grants.
*/
export interface CapturedEntry {
/** Documentation provenance of an exact routed answer; grants no callable authority. */
readonly protectedOrigin?: string;
readonly name: string;
readonly identity: FragmentIdentity;
readonly forms: readonly FragmentForm[];
Expand Down Expand Up @@ -553,8 +555,8 @@ export interface PreparedProfile {
readonly live: () => boolean;
/** End them. Registered by canonical execution before any installation runs. */
readonly revoke: () => void;
/** The completed profile, from the answers this execution resolved. */
seal(answers: ResolvedAnswers): Operation<CapturedProfile>;
/** Seal answers, projecting lifetime wrappers through the execution's private operation. */
seal(answers: ResolvedAnswers, project?: ProtectedBodies["project"]): Operation<CapturedProfile>;
}

/**
Expand Down Expand Up @@ -605,14 +607,17 @@ export function* prepareEvaluationProfile(
capabilities.revoke();
},
// deno-lint-ignore require-yield
*seal(answers: ResolvedAnswers): Operation<CapturedProfile> {
*seal(
answers: ResolvedAnswers,
project?: ProtectedBodies["project"],
): Operation<CapturedProfile> {
// One sealed implementation per name, built before either table is
// sealed. A name that holds two entries — the self-closing spelling in
// `read` and the paired one in `write` — is one component seen from two
// sides, so both entries carry the same object: two guards over one
// answer would be two lifetimes for one implementation, and which of
// them a fragment reached would depend on which table admitted it.
const sealed = sealAnswers(answered, answers, capabilities);
const sealed = sealAnswers(answered, answers, capabilities, project);
return Object.freeze({
read: sealEntries(read, sealed),
write: sealEntries(write, sealed),
Expand Down Expand Up @@ -877,6 +882,7 @@ function spelling(identity: FragmentIdentity): string {

/** One provider-backed name's sealed implementation, shared by every entry. */
interface SealedAnswer {
readonly protectedOrigin?: string;
readonly props: PropsSchema;
readonly definition: FunctionComponentDefinition;
readonly dispatch?: unknown;
Expand All @@ -894,6 +900,7 @@ function sealAnswers(
answered: ReadonlyMap<string, FragmentIdentity>,
answers: ResolvedAnswers,
capabilities: CapturedCapabilities,
project: ProtectedBodies["project"] | undefined,
): ReadonlyMap<string, SealedAnswer> {
const sealed = new Map<string, SealedAnswer>();
for (const name of answered.keys()) {
Expand All @@ -908,11 +915,14 @@ function sealAnswers(
}
const answer = resolved.definition;
const inner = answer.fn;
const guard = bounded(inner, capabilities);
const protectedOrigin = project?.(inner, guard);
sealed.set(
name,
Object.freeze({
props: detach(answer.props),
definition: Object.freeze({ ...answer, fn: bounded(inner, capabilities) }),
definition: Object.freeze({ ...answer, fn: guard }),
...(protectedOrigin === undefined ? {} : { protectedOrigin }),
// The provider's own dispatcher, when its answer has one. The
// definition above runs behind core's lifetime guard, so what a
// selection would read off it is core's function rather than the
Expand Down Expand Up @@ -950,6 +960,7 @@ function sealEntry(entry: PreparedEntry, sealed: ReadonlyMap<string, SealedAnswe
kind: "component-answer" as const,
...described,
definition: answer.definition,
...(answer.protectedOrigin === undefined ? {} : { protectedOrigin: answer.protectedOrigin }),
...(answer.dispatch === undefined ? {} : { dispatch: answer.dispatch }),
});
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2572,6 +2572,7 @@ function* executeDocument(
declared: declaredImports,
guarded: identity.protected,
}),
identity.protectedBodies.project,
);

const authority: ExpansionAuthority = {
Expand Down
46 changes: 32 additions & 14 deletions packages/core/src/expand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3085,14 +3085,26 @@ function* expandFunctionComponent(
// the contextual answer below is. Both report it; only this one is
// beyond reach of the chain that reports it, which is what a component
// choosing an effect from it needs.
const issued = issueInvocation(
expansion.id,
name,
selected,
yield* useScope(),
!selfClosing,
dispatcher,
);
const frame = yield* useScope();
// An authored import must still settle its selection frame, including
// refusing multiple delegations. Only a generated expansion has no such
// table and issues through its narrowed protected route instead.
const issued =
(authority?.identities === undefined
? authority?.protectedBodies?.issue(
definition.fn,
expansion.id,
name,
frame,
!selfClosing,
dispatcher,
)
: undefined) ??
issueInvocation(expansion.id, name, selected, frame, !selfClosing, dispatcher);
const dispatchBody = (body: Operation<unknown>) =>
authority?.invoke === undefined
? body
: authority.invoke(definition.fn, issued.invocation, body);
const projectionState: ProjectionState = {
invocation,
projecting: issued.projecting,
Expand Down Expand Up @@ -3267,23 +3279,29 @@ function* expandFunctionComponent(
}
return renderSegments(outcome.segments);
});
let active = true;
try {
return yield* guarded(validatedProps, issued.invocation, {
syntax: authority?.syntax,
evaluation: authority?.evaluation,
projectContent: lease?.project,
});
return yield* dispatchBody(
guarded(validatedProps, issued.invocation, {
syntax: authority?.syntax,
evaluation: authority?.evaluation,
projectContent: lease?.project,
narrowProtectedBodies: (implementations: Iterable<unknown>) =>
active ? authority?.protectedBodies?.narrow(implementations) : undefined,
}),
);
} finally {
// Closed in the same breath the issuance is: a projector a body
// kept authorizes nothing once that body has finished.
lease?.close();
active = false;
issued.close();
}
}
// Ended in the same breath the body is: an issuance a wrapper kept
// from a finished element authorizes nothing when it is routed here.
try {
return yield* definition.fn(validatedProps, issued.invocation);
return yield* dispatchBody(definition.fn(validatedProps, issued.invocation));
} finally {
issued.close();
}
Expand Down
Loading
Loading