Skip to content

Update dependency @crossplane-org/function-sdk-typescript to ^0.8.0 - #22

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/crossplane-org-function-sdk-typescript-0.x
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/crossplane-org-function-sdk-typescript-0.x

Conversation

@renovate

@renovate renovate Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
@crossplane-org/function-sdk-typescript ^0.5.0 → ^0.8.0 age confidence

Release Notes

crossplane/function-sdk-typescript (@​crossplane-org/function-sdk-typescript)

v0.8.0

Compare Source

v0.8.0 - Update Response Helpers

This is a breaking release. See Migrating below -- the fix is
mechanical.

Overview

Previous to v0.8.0 the helper functions returned inconsistent values: In v0.8.0 they return nothing.

helper declared actual
fatal RunFunctionResponse mutates, returns the same object
normal (inferred void) mutates, returns void
warning (inferred void) mutates, returns void
setDesiredCompositeStatus RunFunctionResponse mutates, returns the same object
setDesiredComposedResources RunFunctionResponse mutates, returns the same object
setContextKey RunFunctionResponse mutates, returns the same object

Installation

npm install @crossplane-org/function-sdk-typescript@0.8.0

What's Changed

Response helpers return void

These nine helpers previously returned the response and now return nothing:

  • fatal
  • setDesiredComposedResources
  • setDesiredResources
  • setDesiredCompositeResource
  • setDesiredCompositeStatus
  • setContextKey
  • setOutput
  • requireSchema
  • requireResource

normal and warning already returned nothing; their signatures now say so
explicitly rather than leaving it inferred.

Unchanged: to(req, ttl?) still builds and returns a response, and update(src, tgt) and updateDesiredComposedResources(cds, res) still return resources and
resource maps. Those operate on resources, not on a response.

Why this shape

Under the class-based FunctionHandler, rsp was a local, so rsp = setDesiredCompositeStatus({ rsp, status }) hid the ambiguity -- reassigning a
local is harmless whether or not the value is new. With ComposeFunction (added
in v0.7.0) rsp is a parameter, and respecting the declared contract meant
introducing a variable that existed only because the signature was ambiguous:

const withStatus = setDesiredCompositeStatus({ rsp, status });
return withStatus; // withStatus === rsp

For comparison, the Go SDK's response.SetContextKey(rsp, key, v) and the Python
SDK's response.set_output(rsp, output) -> None have always worked this way.

Generated protobuf files no longer carry a version stamp

scripts/protoc-gen.sh now strips the // versions: header block that ts-proto
writes into each generated file (#​46).

That block recorded ts-proto's own version and protoc's, so every dependency bump
rewrote three comment lines in each generated file, the protobuf-check CI job saw
changes under src, and the pull request went red until someone regenerated and
committed. The generated code itself was identical. Output now depends only on the
.proto input and the generator flags, which is what the check is meant to police.

The // Code generated by protoc-gen-ts_proto. DO NOT EDIT. and // source: lines
are untouched, and the versions stay recorded in package.json and the workflow's
protoc pin.

For consumers this is a comment-only difference in the shipped files. No generated
type, message or service changed.

Dependency updates

Direct dependencies moved as follows (#​45):

Package From To
pino 10.3.1 10.4.0
ts-proto 2.12.4 2.13.0
@types/node 25.9.8 25.9.9
eslint 10.11.0 10.12.0
typescript-eslint 8.71.0 8.71.1
vitest 5.0.2 5.0.3
@vitest/coverage-v8 5.0.2 5.0.3

pino is the only runtime dependency in that list; the rest are build and test
tooling. Regenerating the protos under ts-proto 2.13.0 produces no diff.

Migrating

Drop the assignment to rsp. The helper already mutated the response you passed it, so
the behavior is unchanged:

-rsp = setDesiredComposedResources(rsp, dcds);
-rsp = setContextKey(rsp, "endpoint", "db.example.com:5432");
+setDesiredComposedResources(rsp, dcds);
+setContextKey(rsp, "endpoint", "db.example.com:5432");

let rsp = to(req) can usually become const rsp = to(req) afterwards, since
nothing reassigns it any more.

Chained or nested calls need restructuring rather than a token deletion:

-return setContextKey(to(req), "key", "value");
+const rsp = to(req);
+setContextKey(rsp, "key", "value");
+return rsp;
A warning for JavaScript callers

TypeScript callers get a compile error on every rsp = setX(...) line
(Type 'void' is not assignable to type 'RunFunctionResponse'), which makes the
migration self-checking.

Plain JavaScript callers get no error. rsp silently becomes undefined,
and the failure surfaces later -- either as Cannot read properties of undefined
on the next helper call, or as a response that never reaches Crossplane. If you
consume this SDK from JavaScript, grep for = setDesired, = setContextKey,
= setOutput, = require, and = fatal( before upgrading.

Not Changed

to() still assigns req.desired into the response rather than copying it, so
rsp.desired === req.desired when the request has desired state, and writes
through rsp.desired are visible on req.desired. This is pre-existing, and the
Go SDK's response.To aliases in exactly the same way. It is documented and
pinned with a test rather than changed.

v0.7.0

Compare Source

v0.7.0 - serve(), a one-call function entrypoint

A function should not have to assemble a gRPC server to run. This release adds
serve(), which turns a function's entrypoint into a single call, and
ComposeFunction, which lets composition logic be a plain function rather than a
class implementing an interface.

This is a minor release: it only adds API. Nothing was removed or changed, so
FunctionHandler implementations and hand-built servers keep working exactly as
they did in v0.6.0.

Installation

npm install @crossplane-org/function-sdk-typescript@0.7.0

What's Changed

serve() — the whole entrypoint

Previously every function shipped its own copy of the same main.ts: parse flags
(usually with commander, a dependency the SDK does not provide), build a pino
logger, construct a FunctionRunner, create a server, start it, and wire up
signal handlers. Roughly sixty lines, identical in every function, and a place for
functions to drift apart on flag names and shutdown behaviour.

That is now one call:

#!/usr/bin/env node

import { serve } from '@crossplane-org/function-sdk-typescript';
import { compose } from './function.js';

serve(compose);

serve() parses the standard flags, builds a logger from --debug, starts the
gRPC server, and shuts down cleanly on SIGINT and SIGTERM. It accepts either a
ComposeFunction or a FunctionHandler, so existing handlers can be passed
directly.

Every function served this way accepts the same flags, with --help:

Usage: main.js [flags]

A Crossplane composition function.

Flags:
      --address <value>               Address to listen for gRPC connections. Default 0.0.0.0:9443.
  -d, --debug                         Emit debug logs.
      --insecure                      Run without mTLS credentials.
      --tls-server-certs-dir <value>  Directory holding tls.key, tls.crt and ca.crt. Default /tls/server.
  -h, --help                          Show this help.

Flags come from node:util's parseArgs, so there is no new runtime dependency.
An unrecognised flag prints the message and a pointer to --help, then exits 2,
rather than surfacing as a stack trace through Node's internals.

serve() takes an options object for the cases the defaults do not cover — name
for the program name in --help, argv, logger, and serverOptions overrides
applied on top of the parsed flags. These mostly matter in tests.

ComposeFunction — composition logic as a plain function

A function can now be written as a function:

import { Resource, type ComposeFunction } from '@crossplane-org/function-sdk-typescript';

export const compose: ComposeFunction = (req, rsp, logger) => {
  rsp.desired.resources['config'] = Resource.fromJSON({
    resource: {
      apiVersion: 'v1',
      kind: 'ConfigMap',
      metadata: { name: 'my-config' },
      data: { key: 'value' },
    },
  });
  return rsp;
};

The response is handed in already initialised from the request, so there is no
call to to() to forget. Its type, ComposeResponse, is a RunFunctionResponse
whose desired is guaranteed present — rsp.desired.resources[name] needs no
non-null assertion, even though the protobuf schema marks desired optional.
Returning the response is required by the signature, so forgetting is a compile
error rather than an empty response at runtime.

FunctionHandler is unchanged and still the right choice when a function needs
the full interface. fromCompose() adapts a ComposeFunction to it.

One thing worth knowing: for every function after the first in a pipeline, the
request already carries desired state, and rsp.desired is then the same object
as req.desired, not a copy. This is inherited from to() and is usually
harmless — a function reads observed state and writes desired state — but do not
rely on req.desired still holding what the previous function left once you have
started writing.

New exports
  • serve(fn, opts?)
  • fromCompose(compose)
  • parseArgs(argv) — the standard flags, for functions that add flags of their own
  • helpText(name)
  • DEFAULT_ADDRESS, DEFAULT_TLS_SERVER_CERTS_DIR
  • Types: ComposeFunction, ComposeResponse, ServeOptions
Documentation

README.md and USAGE.md now lead with serve() and ComposeFunction. The
previous hand-rolled entrypoint — which used commander, never an SDK dependency
— has moved to a "Building the Server Yourself" section for functions that need
to own their process.

Breaking Changes

None.

Upgrading

npm install @crossplane-org/function-sdk-typescript@0.7.0 is enough; nothing
else has to change. To adopt serve(), replace the body of your main.ts with
serve(myHandler) — it accepts your existing FunctionHandler — and drop
commander from your dependencies if it was only there for flag parsing.

Full Changelog: crossplane/function-sdk-typescript@v0.6.0...v0.7.0

v0.6.0

Compare Source

v0.6.0 - Dependency Modernization and fromModel() Fix

A maintenance release. There are no new SDK features; this release picks up two dependency majors, fixes fromModel() so it works with generated model classes, and hardens protobuf decoding.

This is a minor rather than a patch release because kubernetes-models 4 → 5 moves @kubernetes-models/base from 5 to 6, which is visible to consumers.

Installation

npm install @crossplane-org/function-sdk-typescript@0.6.0

What's Changed

fromModel() accepts any model with a toJSON() method

fromModel() previously constrained its argument's toJSON() to return Record<string, unknown>:

// Before (v0.5.0)
export function fromModel<T extends Record<string, unknown>>(
  obj: { toJSON: () => T },
  connectionDetails?: ConnectionDetails,
  ready?: Ready
): Resource

@kubernetes-models/base declares toJSON(): unknown, so model classes generated from CRDs — including the crossplane-models package the Crossplane CLI generates for your project's schemas — did not satisfy that constraint and failed to type-check. The signature is now widened:

// After (v0.6.0)
export function fromModel(
  obj: { toJSON: () => unknown },
  connectionDetails?: ConnectionDetails,
  ready?: Ready
): Resource

Passing a generated model now works directly:

import { fromModel, setDesiredComposedResources } from '@crossplane-org/function-sdk-typescript';
import { ConfigMap } from 'kubernetes-models/v1/ConfigMap';

const cm = new ConfigMap({
  metadata: { name: 'app-config', namespace: 'default' },
  data: { region: 'us-west-2' },
});

setDesiredComposedResources(rsp, { config: fromModel(cm) });

See the Breaking Changes section below if you were passing an explicit type argument.

kubernetes-models 5 (@kubernetes-models/base 6)

The kubernetes-models dependency moves from ^4.5.1 to ^5.0.0, which brings @kubernetes-models/base from 5 to 6. This is the change that makes this release a minor rather than a patch.

It also aligns the SDK with what the Crossplane CLI's generated crossplane-models package pins, so projects using both end up with a single copy of @kubernetes-models/base rather than one hoisted and one nested.

If you pass typed models to fromModel(), bump your own kubernetes-models dependency to ^5.0.0.

Protobuf decoding hardening

The generated protobuf code was regenerated with ts-proto 2.12.1, which adds two robustness improvements to the decode path used for every RunFunctionRequest:

  • Recursion depth limit — nested message decoding now throws protobuf decode recursion limit exceeded beyond 100 levels, instead of exhausting the stack.
  • Prototype-pollution hardening — map entries are constructed with Object.defineProperty rather than direct property assignment.

These are upstream generator changes; no SDK API is affected.

ts-deepmerge 8

ts-deepmerge moves from ^7.0.3 to ^8.0.0. Its return types are tighter, which let three internal casts be removed. The merge semantics of update() are unchanged — arrays are still replaced by default, and { mergeArrays: true } still concatenates.

Build toolchain: TypeScript 7

The SDK now builds with TypeScript 7, the native compiler. This is build-time only and requires no change from consumers.

Because TypeScript 7 no longer exposes the JavaScript compiler API that typescript-eslint's type-aware rules depend on, the repository installs both compilers side by side: TypeScript 7 as @typescript/native (providing tsc), and TypeScript 6 aliased as typescript (providing tsc6). This arrangement is temporary and will be removed once typescript-eslint supports TypeScript 7.

Verified against the published package: @crossplane-org/function-sdk-typescript@0.6.0 type-checks and runs in a clean project on TypeScript 5.9.3 with skipLibCheck: false.

Other maintenance
  • ESLint 10 and @eslint/js 10, with rethrown errors now preserving the original via { cause }
  • flatted updated to address a vulnerability
  • GitHub Actions updated and pinned to digests

Requirements

  • Node.js 18+
  • TypeScript 5.9+ (unchanged)
  • kubernetes-models ^5.0.0, if you pass typed models to fromModel()
  • Compatible with Crossplane Function Runtime API v1
  • Requires Crossplane v2.2+ for capability advertisement, required resources, and required schemas

Breaking Changes

Two narrow ones. Most functions will need no changes at all.

1. fromModel() no longer takes a type parameter.

The generic was removed when the signature was widened. An explicit type argument is now an error:

// No longer compiles: "Expected 0 type arguments, but got 1"
fromModel<MyModelShape>(model);

// Use
fromModel(model);

Calls that relied on inference — which is nearly all of them — are unaffected.

2. kubernetes-models 4 → 5.

If your function passes typed models to fromModel(), update your dependency to ^5.0.0. Staying on 4.x leaves you with two copies of @kubernetes-models/base and can produce type mismatches at the fromModel() boundary.

Migration Guide

  1. Update the SDK:
    npm install @crossplane-org/function-sdk-typescript@^0.6.0
  2. If you use kubernetes-models, update it to ^5.0.0:
    npm install kubernetes-models@^5.0.0
  3. Remove any explicit type arguments from fromModel() calls.

No other changes are required. Your TypeScript version does not need to change.

Documentation

📖 README.md - Complete documentation
📖 USAGE.md - Usage guide

Full Changelog

Full Changelog: crossplane/function-sdk-typescript@v0.5.0...v0.6.0

Pull Requests
  • #​31: release: v0.6.0
  • #​30: update flatted to address vulnerability
  • #​29: build: run TypeScript 7 alongside TypeScript 6
  • #​28: chore(deps): update dependency @​eslint/js to v10
  • #​27: chore(deps): update all non-major dependencies
  • #​26: Accept models whose toJSON() returns unknown in fromModel
  • #​25: fix(deps): update dependency ts-deepmerge to v8
  • #​24: fix(deps): update dependency kubernetes-models to v5
  • #​23: chore(deps): update github actions (major)
  • #​20: chore(deps): update github actions
Commits
  • release: v0.6.0 (ce7c3a6)
  • update flatted to address vuln (9c3b9fe)
  • fix(lint): attach cause to rethrown errors (ad569e7)
  • chore(deps): update dependency @​eslint/js to v10 (8efb047)
  • build: run TypeScript 7 alongside TypeScript 6 (16836f3)
  • chore: regenerate protobuf and drop redundant merge casts (70d4145)
  • chore(deps): update all non-major dependencies (4ec2940)
  • Accept models whose toJSON() returns unknown in fromModel (dcdbfb4)
  • chore(deps): update github actions (29c1a6e)
  • fix(deps): update dependency ts-deepmerge to v8 (bf776fc)
  • fix(deps): update dependency kubernetes-models to v5 (0e5564b)
  • chore(deps): update github actions (f6bfb0d)

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate
renovate Bot force-pushed the renovate/crossplane-org-function-sdk-typescript-0.x branch from 51954cb to 5494baf Compare August 29, 2026 08:28
@renovate renovate Bot changed the title Update dependency @crossplane-org/function-sdk-typescript to ^0.6.0 Update dependency @crossplane-org/function-sdk-typescript to ^0.7.0 Aug 29, 2026
@renovate
renovate Bot force-pushed the renovate/crossplane-org-function-sdk-typescript-0.x branch from 5494baf to 498b352 Compare October 8, 2026 20:48
@renovate renovate Bot changed the title Update dependency @crossplane-org/function-sdk-typescript to ^0.7.0 Update dependency @crossplane-org/function-sdk-typescript to ^0.8.0 Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants