From e36fac073d0530f817623df73c7b91b0b5fc65c8 Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Thu, 5 Mar 2026 18:11:20 +0100 Subject: [PATCH 1/3] update docs and package metadata Signed-off-by: Steven Borrelli --- README.md | 110 ++++++++++++++++++++++++++++++++++++++++++---- package-lock.json | 4 +- package.json | 2 +- 3 files changed, 105 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 023cee1..682fb92 100644 --- a/README.md +++ b/README.md @@ -224,8 +224,14 @@ import { getObservedComposedResources, getInput, getContextKey, + getRequiredResource, getRequiredResources, + getRequiredSchema, + getRequiredSchemas, getCredentials, + advertiseCapabilities, + hasCapability, + Capability, } from "@crossplane-org/function-sdk-typescript"; // Get the observed composite resource (XR) @@ -246,11 +252,28 @@ const input = getInput(req); // Get context value from previous function const [value, exists] = getContextKey(req, "my-key"); -// Get required resources +// Get a specific required resource by name +const [resources, resolved] = getRequiredResource(req, "app-config"); + +// Get all required resources const required = getRequiredResources(req); +// Get a specific required schema by name +const [schema, resolved] = getRequiredSchema(req, "xr-schema"); + +// Get all required schemas +const schemas = getRequiredSchemas(req); + // Get credentials const creds = getCredentials(req, "aws-creds"); + +// Check if Crossplane advertises capabilities (v2.2+) +if (advertiseCapabilities(req)) { + // Check for specific capability support + if (hasCapability(req, Capability.CAPABILITY_REQUIRED_RESOURCES)) { + // Use required resources feature + } +} ``` #### Response Helpers @@ -268,7 +291,8 @@ import { normal, fatal, warning, - update, + requireResource, + requireSchema, DEFAULT_TTL, } from "@crossplane-org/function-sdk-typescript"; @@ -295,8 +319,16 @@ normal(rsp, "Success message"); warning(rsp, "Warning message"); fatal(rsp, "Fatal error message"); -// Update a resource by merging -const updated = update(sourceResource, targetResource); +// Request Crossplane fetch a resource (available in next invocation) +rsp = requireResource(rsp, "app-config", { + apiVersion: "v1", + kind: "ConfigMap", + matchName: "my-config", + namespace: "default" +}); + +// Request Crossplane fetch a schema (available in next invocation) +rsp = requireSchema(rsp, "xr-schema", "example.org/v1", "MyResource"); ``` #### Resource Helpers @@ -311,6 +343,8 @@ import { fromObject, toObject, newDesiredComposed, + update, + getCondition, } from "@crossplane-org/function-sdk-typescript"; // Create a Resource from a plain object @@ -329,6 +363,35 @@ const plainObj = asObject(struct); // Create a new empty DesiredComposed resource const desired = newDesiredComposed(); + +// Deep merge updates into a resource (arrays replaced by default) +update(resource, { + resource: { + spec: { + forProvider: { + region: "us-west-2", + tags: ["env:prod"] // Replaces existing tags + } + } + } +}); + +// Or merge with array concatenation +update(resource, { + resource: { + spec: { + forProvider: { + tags: ["new-tag"] // Appended to existing tags + } + } + } +}, { mergeArrays: true }); + +// Get a status condition from a resource +const readyCondition = getCondition(resource.resource, "Ready"); +if (readyCondition.status === "True") { + console.log("Resource is ready"); +} ``` ### Error Handling @@ -519,6 +582,10 @@ import { ObservedComposed, DesiredComposed, ConnectionDetails, + MergeOptions, + + // Status condition types + Condition as ResourceCondition, // Protocol buffer types Severity, @@ -531,6 +598,11 @@ import { Resources, Credentials, CredentialData, + Requirements, + ResourceSelector, + SchemaSelector, + Schema, + Capability, // Runtime types ServerOptions, @@ -539,21 +611,43 @@ import { ### Core Functions +#### Response Functions + - **`to(req, ttl?)`** - Initialize a response from a request - **`normal(rsp, message)`** - Add a normal (info) result - **`warning(rsp, message)`** - Add a warning result - **`fatal(rsp, message)`** - Add a fatal error result +- **`setDesiredComposedResources(rsp, resources)`** - Set desired composed resources +- **`setDesiredCompositeStatus({rsp, status})`** - Update XR status +- **`setContextKey(rsp, key, value)`** - Set context for next function +- **`setOutput(rsp, output)`** - Set function output +- **`requireResource(rsp, name, selector)`** - Request Crossplane fetch resources +- **`requireSchema(rsp, name, apiVersion, kind)`** - Request Crossplane fetch schemas + +#### Request Functions + - **`getObservedCompositeResource(req)`** - Get the observed XR - **`getDesiredCompositeResource(req)`** - Get the desired XR - **`getDesiredComposedResources(req)`** - Get desired composed resources - **`getObservedComposedResources(req)`** - Get observed composed resources -- **`setDesiredComposedResources(rsp, resources)`** - Set desired composed resources -- **`setDesiredCompositeStatus({rsp, status})`** - Update XR status -- **`setContextKey(rsp, key, value)`** - Set context for next function - **`getContextKey(req, key)`** - Get context from previous function - **`getInput(req)`** - Get function input configuration -- **`getRequiredResources(req)`** - Get required resources +- **`getRequiredResource(req, name)`** - Get a required resource by name +- **`getRequiredResources(req)`** - Get all required resources (deprecated) +- **`getRequiredSchema(req, name)`** - Get a required schema by name +- **`getRequiredSchemas(req)`** - Get all required schemas - **`getCredentials(req, name)`** - Get credentials by name (throws error if not found) +- **`advertiseCapabilities(req)`** - Check if Crossplane advertises capabilities +- **`hasCapability(req, capability)`** - Check for a specific capability + +#### Resource Functions + +- **`fromObject(obj, connectionDetails?, ready?)`** - Create Resource from plain object +- **`fromModel(model, connectionDetails?, ready?)`** - Create Resource from kubernetes-models +- **`toObject(resource)`** - Extract plain object from Resource +- **`update(resource, source, options?)`** - Deep merge data into a Resource +- **`getCondition(resource, type)`** - Extract a status condition by type +- **`newDesiredComposed()`** - Create an empty DesiredComposed resource See [USAGE.md](USAGE.md) for detailed API documentation and examples. diff --git a/package-lock.json b/package-lock.json index 17a9e78..ec63b20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@crossplane-org/function-sdk-typescript", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@crossplane-org/function-sdk-typescript", - "version": "0.4.0", + "version": "0.5.0", "license": "Apache-2.0", "dependencies": { "@grpc/grpc-js": "^1.14.3", diff --git a/package.json b/package.json index b47c671..ddcae3b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@crossplane-org/function-sdk-typescript", - "version": "0.4.0", + "version": "0.5.0", "description": "A Crossplane Function SDK for Typescript", "keywords": [ "crossplane", From eb47b306f8f563c6dd86a1372ba5aebf3e3fa617 Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Thu, 5 Mar 2026 18:26:09 +0100 Subject: [PATCH 2/3] update readme Signed-off-by: Steven Borrelli --- README.md | 2 +- src/proto/typeRegistry.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/proto/typeRegistry.ts diff --git a/README.md b/README.md index 682fb92..8668210 100644 --- a/README.md +++ b/README.md @@ -633,7 +633,7 @@ import { - **`getContextKey(req, key)`** - Get context from previous function - **`getInput(req)`** - Get function input configuration - **`getRequiredResource(req, name)`** - Get a required resource by name -- **`getRequiredResources(req)`** - Get all required resources (deprecated) +- **`getRequiredResources(req)`** - Get all required resources - **`getRequiredSchema(req, name)`** - Get a required schema by name - **`getRequiredSchemas(req)`** - Get all required schemas - **`getCredentials(req, name)`** - Get credentials by name (throws error if not found) diff --git a/src/proto/typeRegistry.ts b/src/proto/typeRegistry.ts new file mode 100644 index 0000000..9b4c292 --- /dev/null +++ b/src/proto/typeRegistry.ts @@ -0,0 +1,27 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v2.11.4 +// protoc v6.33.4 + +/* eslint-disable */ +import type { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; + +export interface MessageType { + $type: Message["$type"]; + encode(message: Message, writer?: BinaryWriter): BinaryWriter; + decode(input: BinaryReader | Uint8Array, length?: number): Message; + fromJSON(object: any): Message; + toJSON(message: Message): unknown; + fromPartial(object: DeepPartial): Message; +} + +export type UnknownMessage = { $type: string }; + +export const messageTypeRegistry = new Map(); + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends {} ? { [K in Exclude]?: DeepPartial } + : Partial; From 1de6cb968bb4edf91a71a95c633a172dedd25b23 Mon Sep 17 00:00:00 2001 From: Steven Borrelli Date: Thu, 5 Mar 2026 22:14:45 +0100 Subject: [PATCH 3/3] remove typeRegistry.ts Signed-off-by: Steven Borrelli --- src/proto/typeRegistry.ts | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/proto/typeRegistry.ts diff --git a/src/proto/typeRegistry.ts b/src/proto/typeRegistry.ts deleted file mode 100644 index 9b4c292..0000000 --- a/src/proto/typeRegistry.ts +++ /dev/null @@ -1,27 +0,0 @@ -// Code generated by protoc-gen-ts_proto. DO NOT EDIT. -// versions: -// protoc-gen-ts_proto v2.11.4 -// protoc v6.33.4 - -/* eslint-disable */ -import type { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire"; - -export interface MessageType { - $type: Message["$type"]; - encode(message: Message, writer?: BinaryWriter): BinaryWriter; - decode(input: BinaryReader | Uint8Array, length?: number): Message; - fromJSON(object: any): Message; - toJSON(message: Message): unknown; - fromPartial(object: DeepPartial): Message; -} - -export type UnknownMessage = { $type: string }; - -export const messageTypeRegistry = new Map(); - -type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; -export type DeepPartial = T extends Builtin ? T - : T extends globalThis.Array ? globalThis.Array> - : T extends ReadonlyArray ? ReadonlyArray> - : T extends {} ? { [K in Exclude]?: DeepPartial } - : Partial;