v0.5.0 - Required Resources, Schemas, and Capability Support
This release adds major new features for working with required resources and schemas, bringing the TypeScript SDK to feature parity with the Python SDK. Functions can now request Crossplane fetch additional resources and OpenAPI schemas, and check Crossplane's capabilities before using advanced features.
Installation
npm install @crossplane-org/function-sdk-typescript@0.5.0What's New
Required Resources Support
Dynamically Fetch Additional Resources: Functions can now request that Crossplane fetch specific Kubernetes resources and include them in subsequent requests. This enables functions to read ConfigMaps, Secrets, and other cluster resources to inform composition logic.
Response: Request Resources
Use requireResource() to tell Crossplane which resources to fetch:
import { requireResource } from '@crossplane-org/function-sdk-typescript';
// Match a specific ConfigMap by name
rsp = requireResource(rsp, "app-config", {
apiVersion: "v1",
kind: "ConfigMap",
matchName: "my-app-config",
namespace: "production"
});
// Match all Secrets with specific labels
rsp = requireResource(rsp, "db-secrets", {
apiVersion: "v1",
kind: "Secret",
matchLabels: {
labels: {
app: "database",
tier: "backend"
}
},
namespace: "production"
});Request: Retrieve Required Resources
In the next function invocation, retrieve the fetched resources:
import { getRequiredResource } from '@crossplane-org/function-sdk-typescript';
const [resources, resolved] = getRequiredResource(req, "app-config");
if (!resolved) {
console.log("Resource requirement not yet resolved by Crossplane");
} else if (resources.length === 0) {
console.log("Resource requirement resolved but no resources found");
} else {
console.log("Found resources:", resources);
resources.forEach(r => console.log(r.resource));
}The selector supports:
- Match by name: Fetch a specific resource with
matchName - Match by labels: Fetch all resources matching label selectors with
matchLabels - Namespace scoping: Specify
namespacefor namespaced resources, or omit for cluster-scoped resources - Cross-namespace: Omit
namespacewhen matching by labels to search all namespaces
Required Schemas Support
Access OpenAPI Schemas: Functions can now request OpenAPI v3 schemas for resource kinds. This enables validation, default value application, and schema-aware composition logic.
Response: Request Schemas
Use requireSchema() to tell Crossplane which schemas to fetch:
import { requireSchema } from '@crossplane-org/function-sdk-typescript';
// Request the OpenAPI schema for an XR type
rsp = requireSchema(rsp, "xr-schema", "example.org/v1", "MyResource");
// Request schema for a composed resource type
rsp = requireSchema(rsp, "bucket-schema", "s3.aws.upbound.io/v1beta1", "Bucket");Request: Retrieve Required Schemas
In the next function invocation, access the schemas:
import { getRequiredSchema, getRequiredSchemas } from '@crossplane-org/function-sdk-typescript';
// Get a specific schema
const [schema, resolved] = getRequiredSchema(req, "xr-schema");
if (!resolved) {
console.log("Schema not yet resolved by Crossplane");
} else if (!schema) {
console.log("Schema resolved but not found (kind may not exist)");
} else {
console.log("Schema properties:", schema.properties);
console.log("Required fields:", schema.required);
}
// Get all required schemas
const schemas = getRequiredSchemas(req);
for (const [name, schema] of Object.entries(schemas)) {
if (schema) {
console.log(`Schema ${name}:`, schema);
} else {
console.log(`Schema ${name} was requested but not found`);
}
}For CRDs, Crossplane returns the spec.versions[].schema.openAPIV3Schema field. If Crossplane cannot find a schema for the requested kind, the schema will be undefined (with resolved returning true).
Capability Detection
Check Crossplane Features: Functions can now detect which capabilities Crossplane supports, enabling graceful degradation and conditional feature usage.
import { advertiseCapabilities, hasCapability, Capability } from '@crossplane-org/function-sdk-typescript';
// Check if Crossplane advertises capabilities (v2.2+)
if (!advertiseCapabilities(req)) {
console.log("Crossplane version predates capability advertisement");
} else {
// Check for specific capabilities
if (hasCapability(req, Capability.CAPABILITY_REQUIRED_SCHEMAS)) {
requireSchema(rsp, "xr", xrApiVersion, xrKind);
}
if (hasCapability(req, Capability.CAPABILITY_REQUIRED_RESOURCES)) {
requireResource(rsp, "config", configSelector);
}
}Available capabilities:
CAPABILITY_CAPABILITIES- Crossplane advertises its capabilitiesCAPABILITY_REQUIRED_RESOURCES- Support for required resourcesCAPABILITY_REQUIRED_SCHEMAS- Support for required schemasCAPABILITY_WATCHED_RESOURCES- Support for watched resources (deprecated)
Crossplane v2.2 and later advertise their capabilities in the request metadata. For older versions, advertiseCapabilities() returns false and hasCapability() will always return false, even for features the older Crossplane may support.
Enhanced Resource Utilities
Merge and Update Resources: New helper functions for working with resource conditions and updates.
Update Resources with Merge
The update() function performs deep merges of resource data:
import { update } from '@crossplane-org/function-sdk-typescript';
const bucket = getDesiredComposedResources(req)["my-bucket"];
if (bucket) {
// Update specific fields while preserving others
// Arrays are replaced by default (matches Python SDK behavior)
update(bucket, {
resource: {
spec: {
forProvider: {
region: "us-west-2",
tags: ["env:prod", "team:platform"] // Replaces existing tags
}
}
}
});
}
// Optionally concatenate arrays instead of replacing them
update(bucket, {
resource: {
spec: {
forProvider: {
tags: ["new-tag"] // Will be appended to existing tags
}
}
}
}, { mergeArrays: true });By default, arrays are replaced (matching Python SDK semantics). Set mergeArrays: true to concatenate arrays instead.
Get Status Conditions
The getCondition() function extracts Kubernetes status conditions:
import { getCondition } from '@crossplane-org/function-sdk-typescript';
const oxr = getObservedCompositeResource(req);
if (oxr?.resource) {
const readyCondition = getCondition(oxr.resource, "Ready");
if (readyCondition.status === "True") {
console.log("Resource is ready");
} else if (readyCondition.status === "False") {
console.log("Resource not ready:", readyCondition.message);
} else {
console.log("Ready status unknown");
}
}
// Check if a composed resource is synced
const bucket = observedResources["my-bucket"];
if (bucket?.resource) {
const synced = getCondition(bucket.resource, "Synced");
console.log("Sync status:", synced.status, synced.reason);
}Returns a condition with status "Unknown" if the requested condition type is not found.
New Types and Exports
This release adds several new TypeScript types and exports:
Request helpers:
getRequiredResource()- Get a required resource by namegetRequiredResources()- Get all required resourcesgetRequiredSchema()- Get a required schema by namegetRequiredSchemas()- Get all required schemasadvertiseCapabilities()- Check if Crossplane advertises capabilitieshasCapability()- Check for a specific capability
Response helpers:
requireResource()- Request Crossplane fetch resourcesrequireSchema()- Request Crossplane fetch schemas
Resource utilities:
update()- Deep merge resource data with configurable array handlinggetCondition()- Extract status conditions from resourcesCondition- TypeScript interface for status conditionsMergeOptions- Options for controlling merge behavior
Protocol buffer types:
Capability- Enum of Crossplane capabilitiesResourceSelector- Selector for matching required resourcesSchemaSelector- Selector for matching required schemasRequirements- Protocol buffer message for requirements
Protobuf Updates
Updated Function Runtime API: Regenerated protobuf TypeScript files from the latest Crossplane Function Runtime API, adding support for:
- Required resources (
requiredResourcesfield) - Required schemas (
requiredSchemasfield) - Capability advertisement (
meta.capabilitiesfield) - Resource requirements (
requirements.resourcesfield) - Schema requirements (
requirements.schemasfield)
The protobuf updates add 572 lines of new types and message handling code for these features.
Feature Parity with Python SDK
This release brings the TypeScript SDK to feature parity with the Python SDK (v0.8+) for:
- ✅ Required resources with selectors (name and label-based)
- ✅ Required schemas with OpenAPI v3 support
- ✅ Capability detection and advertisement
- ✅ Resource update with configurable merge semantics
- ✅ Status condition extraction
- ✅ Comprehensive test coverage for all new features
Testing
All new features include comprehensive test coverage:
- ✅ Required resource fetching and resolution
- ✅ Required schema fetching and resolution
- ✅ Capability detection and checking
- ✅ Resource update and merge operations (with and without array merging)
- ✅ Status condition extraction
- ✅ Edge cases and error handling
- ✅ Full test suite passing with 2,985 new lines of test coverage
- ✅ CI/CD pipeline validates all changes
Documentation
📖 README.md - Complete documentation
📖 USAGE.md - Usage guide
Requirements
- Node.js 18+
- TypeScript 5.9+
- Compatible with Crossplane Function Runtime API v1
- Requires Crossplane v2.2+ for capability advertisement
- Required resources and schemas supported in Crossplane v2.2+
Breaking Changes
None. This release is fully backward compatible with v0.4.0.
Full Changelog
Full Changelog: v0.4.0...v0.5.0
Pull Requests
- #15: Match python sdk - Feature parity for required resources, schemas, and capabilities
- #14: Add Required resources and schemas
Commits
- Merge pull request #15 from stevendborrelli/match-python-sdk (e5816ba)
- update based on copilot review (39dd3c2)
- npm run format (04e9c85)
- add capability support (32b0cfc)
- update test and check for proto resource type (b3c7c70)
- sync with python sdk (b0ae0ef)
- Merge pull request #14 from stevendborrelli/required-resources (257b405)
- formt and export capability (b9447a8)
- fix copilot review issues (a69015d)
- drop error (515f1f0)
- simplify function return (0a79b3e)
- add support for required resources (bf39a2f)
Migration Guide
No migration required. All existing code continues to work unchanged. To use the new features:
-
Required Resources: Add
requireResource()calls to your response to request resources, then usegetRequiredResource()in subsequent requests to access them. -
Required Schemas: Add
requireSchema()calls to your response to request schemas, then usegetRequiredSchema()in subsequent requests to access them. -
Capability Detection: Use
advertiseCapabilities()andhasCapability()to conditionally enable features based on Crossplane version. -
Resource Updates: Replace manual object spreading with the
update()function for safer deep merges with configurable array handling. -
Status Conditions: Use
getCondition()instead of manually traversingresource.status.conditionsarrays.
Example function using new features:
import {
FunctionRunner,
getObservedCompositeResource,
getRequiredResource,
getRequiredSchema,
requireResource,
requireSchema,
setDesiredComposedResources,
advertiseCapabilities,
hasCapability,
Capability,
update,
getCondition,
} from '@crossplane-org/function-sdk-typescript';
const runner = new FunctionRunner();
runner.addFunction('compose', async (req, log) => {
const rsp = await runner.createResponse(req);
// Check capabilities before using advanced features
if (advertiseCapabilities(req) && hasCapability(req, Capability.CAPABILITY_REQUIRED_RESOURCES)) {
// Request a ConfigMap in the first invocation
requireResource(rsp, "app-config", {
apiVersion: "v1",
kind: "ConfigMap",
matchName: "my-config",
namespace: "default"
});
// In subsequent invocations, use the config
const [configs, resolved] = getRequiredResource(req, "app-config");
if (resolved && configs.length > 0) {
const config = configs[0];
log.info({ config: config.resource }, "Using config from cluster");
// Check if the config is ready
const ready = getCondition(config.resource, "Ready");
if (ready.status === "True") {
// Use config data to compose resources
const bucket = desiredResources["my-bucket"];
if (bucket) {
update(bucket, {
resource: {
spec: {
forProvider: {
region: config.resource?.data?.region || "us-east-1"
}
}
}
});
}
}
}
}
return rsp;
});