Add activated workspace reflection and Studio definition watches

Expose bounded snapshot-bound definition reads and latest-only watches from the serving checked bundle. Add checked reflection dependency ports, generated SDK bindings and selector-scoped invocation authority.

Move Studio factory, contract and query discovery to public reflection inventories. Share an active-definition watch across canvas and standalone routes and invalidate/reload on epoch changes instead of polling GetWorkspace.

Document the API and deferred scope; cover compiler/codegen, SDK injection, transport authorization, activation publication, pagination and reconnect behavior. Keep all work local; no deployment or publication.
This commit is contained in:
Timothy J. Aveni
2026-09-19 10:18:47 -07:00
parent 1aa55f7a35
commit 757531bb63
20 changed files with 2870 additions and 2105 deletions
+2 -1
View File
@@ -22,7 +22,7 @@ import {
export type GenericDependencyPort = Omit<DependencyPort, "requirement"> & {
requirement:
| Extract<DependencyPort["requirement"], { kind: "query" }>
| Extract<DependencyPort["requirement"], { kind: "query" | "reflection" }>
| {
kind: "state";
valueType: ValueTypeExpression;
@@ -80,6 +80,7 @@ export const specializePackageExport = (
const ports: DependencyPort[] = definition.dependencyPorts.map((port) => {
const requirement = port.requirement;
switch (requirement.kind) {
case "reflection":
case "query":
return { ...port, requirement };
case "state":
+2
View File
@@ -223,6 +223,7 @@ export type PackageReceiverRequirement =
};
export type DependencyPortRequirement =
| { kind: "reflection"; selectors: string[] }
| { kind: "query"; packageRevisionId: PackageRevisionId; queryId: string }
| {
kind: "state";
@@ -292,6 +293,7 @@ export interface PackageRevision {
}
export type DependencyBinding =
| { kind: "reflection"; selectors: string[]; via?: never }
| {
kind: "query";
packageRevisionId: PackageRevisionId;
+24
View File
@@ -998,6 +998,16 @@ const validatePackages = (
for (const [portIndex, port] of entry.dependencyPorts.entries()) {
const portPath = `${exportPath}.dependencyPorts[${portIndex}]`;
switch (port.requirement.kind) {
case "reflection":
if (
!port.requirement.selectors.length ||
new Set(port.requirement.selectors).size !== port.requirement.selectors.length ||
port.requirement.selectors.some(
(kind) => !["active", "atoms", "interfaces", "conformances", "queries", "indexes"].includes(kind),
)
)
issue(issues, "invalid-dependency-binding", portPath, "Reflection requires distinct supported selectors");
break;
case "query":
if (
!indexes.packages
@@ -1162,6 +1172,20 @@ const validateBoundDependencies = (
continue;
}
switch (requirement.kind) {
case "reflection": {
const selected = binding as Extract<DependencyBinding, { kind: "reflection" }>;
if (
selected.via ||
JSON.stringify([...selected.selectors].sort()) !== JSON.stringify([...requirement.selectors].sort())
)
issue(
issues,
"invalid-dependency-binding",
path,
"Reflection requires an explicit workspace binding with exactly the declared selectors",
);
break;
}
case "query": {
const selected = binding as Extract<DependencyBinding, { kind: "query" }>;
const query = params.indexes.packages