52803dda05
Add kinded parameters, capability bounds, Self, aliases and closed application identities. Check generic implementations universally and build candidate-specific codecs and descriptors from immutable schemas. Preserve lexical aliases and exact dispatch identities in package and host bindings. Add an imperative CRUD+index domain scaffold with explicit soft-deletion semantics, source/codegen regression coverage, installed CLI tests and an authoring guide. Existing Web Studio opaque props and class-level create-menu migration are separate from the implemented language core.
142 lines
5.1 KiB
TypeScript
142 lines
5.1 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import {
|
|
capabilityId,
|
|
type PackageExport,
|
|
type PackageExportId,
|
|
type DependencyPort,
|
|
type PackageRevision,
|
|
} from "./types.js";
|
|
import {
|
|
TypeSubstitution,
|
|
GenericTypeError,
|
|
bindTypeParameters,
|
|
canonicalTypeArgument,
|
|
type TypeParameter,
|
|
type ValueTypeExpression,
|
|
type ObjectTypeExpression,
|
|
type InterfaceApplicationExpression,
|
|
type GenericTypeEnvironment,
|
|
type ClosedTypeArgument,
|
|
type ValueAliasDefinition,
|
|
} from "./generics.js";
|
|
|
|
export type GenericDependencyPort = Omit<DependencyPort, "requirement"> & {
|
|
requirement:
|
|
| {
|
|
kind: "state";
|
|
valueType: ValueTypeExpression;
|
|
primitives: Extract<DependencyPort["requirement"], { kind: "state" }>["primitives"];
|
|
}
|
|
| {
|
|
kind: "edge";
|
|
target: ObjectTypeExpression;
|
|
cardinality: Extract<DependencyPort["requirement"], { kind: "edge" }>["cardinality"];
|
|
primitives: Extract<DependencyPort["requirement"], { kind: "edge" }>["primitives"];
|
|
}
|
|
| { kind: "interface"; application: InterfaceApplicationExpression }
|
|
| { kind: "constructor"; target: ObjectTypeExpression; inputType: ValueTypeExpression };
|
|
};
|
|
|
|
export interface GenericPackageExport {
|
|
id: PackageExportId;
|
|
displayName: string;
|
|
parameters: TypeParameter[];
|
|
kind: "operation" | "function";
|
|
inputType: ValueTypeExpression;
|
|
outputType: ValueTypeExpression;
|
|
eventType?: ValueTypeExpression;
|
|
mode?: Extract<PackageExport, { kind: "operation" }>["mode"];
|
|
receiverRequirement:
|
|
| { kind: "any-object" }
|
|
| { kind: "target"; target: ObjectTypeExpression }
|
|
| { kind: "interfaces"; interfaces: InterfaceApplicationExpression[] };
|
|
dependencyPorts: GenericDependencyPort[];
|
|
aliases: ValueAliasDefinition[];
|
|
}
|
|
|
|
/** A closed manifest is a Nix build input, never a mutable source checkout. */
|
|
export const specializePackageExport = (
|
|
pkg: PackageRevision,
|
|
definition: GenericPackageExport,
|
|
arguments_: ClosedTypeArgument[],
|
|
environment: GenericTypeEnvironment,
|
|
): PackageExport => {
|
|
const lexical = { ...environment, aliases: new Map(definition.aliases.map((alias) => [alias.id, alias])) };
|
|
const bindings = bindTypeParameters(definition.parameters, arguments_, lexical, definition.displayName);
|
|
const substitution = new TypeSubstitution({ ...lexical, arguments: bindings });
|
|
const digest = createHash("sha256")
|
|
.update(
|
|
JSON.stringify([
|
|
"quixos-package-specialization-v1",
|
|
pkg.revisionId,
|
|
[pkg.source.repository, pkg.source.commit],
|
|
definition.id,
|
|
arguments_.map(canonicalTypeArgument),
|
|
environment.self ?? null,
|
|
]),
|
|
)
|
|
.digest("hex");
|
|
const ports: DependencyPort[] = definition.dependencyPorts.map((port) => {
|
|
const requirement = port.requirement;
|
|
switch (requirement.kind) {
|
|
case "state":
|
|
return { ...port, requirement: { ...requirement, valueType: substitution.value(requirement.valueType) } };
|
|
case "edge":
|
|
return { ...port, requirement: { ...requirement, target: substitution.object(requirement.target) } };
|
|
case "interface":
|
|
return {
|
|
...port,
|
|
requirement: { kind: "interface", interfaceRevisionId: substitution.application(requirement.application) },
|
|
};
|
|
case "constructor": {
|
|
const target = substitution.object(requirement.target);
|
|
if (target.kind !== "atom")
|
|
throw new GenericTypeError(
|
|
"constructor-target",
|
|
port.displayName,
|
|
"Constructor ports require a concrete atom argument, not an interface view",
|
|
);
|
|
return {
|
|
...port,
|
|
requirement: {
|
|
kind: "constructor",
|
|
atomId: target.atomId,
|
|
inputType: substitution.value(requirement.inputType),
|
|
},
|
|
};
|
|
}
|
|
}
|
|
});
|
|
const common = {
|
|
id: capabilityId.packageExport(`export-application:sha256:${digest}`),
|
|
displayName: `${definition.displayName}$${digest}`,
|
|
inputType: substitution.value(definition.inputType),
|
|
outputType: substitution.value(definition.outputType),
|
|
dependencyPorts: ports,
|
|
application: {
|
|
exportId: definition.id,
|
|
arguments: structuredClone(arguments_),
|
|
...(environment.self ? { self: environment.self } : {}),
|
|
},
|
|
};
|
|
if (definition.kind === "function") return { ...common, kind: "function" };
|
|
const receiver = definition.receiverRequirement;
|
|
const target = receiver.kind === "target" ? substitution.object(receiver.target) : undefined;
|
|
return {
|
|
...common,
|
|
kind: "operation",
|
|
mode: definition.mode!,
|
|
...(definition.eventType ? { eventType: substitution.value(definition.eventType) } : {}),
|
|
receiverRequirement: target
|
|
? target.kind === "atom"
|
|
? { kind: "exact-atom", atomId: target.atomId }
|
|
: { kind: "all-interfaces", interfaceRevisionIds: [target.interfaceRevisionId] }
|
|
: receiver.kind === "interfaces"
|
|
? {
|
|
kind: "all-interfaces",
|
|
interfaceRevisionIds: receiver.interfaces.map((value) => substitution.application(value)),
|
|
}
|
|
: { kind: "any-object" },
|
|
};
|
|
};
|