@crossplane-org/function-sdk-typescript 0.6.0 → 0.7.0-20260829081314-5f2a0f216756
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/serve/serve.d.ts +142 -0
- package/dist/serve/serve.d.ts.map +1 -0
- package/dist/serve/serve.js +192 -0
- package/dist/serve/serve.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,7 +20,46 @@ npm install @crossplane-org/function-sdk-typescript
|
|
|
20
20
|
|
|
21
21
|
### Creating Your Function
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
A function is a plain function given the request and a response to fill in:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import {
|
|
27
|
+
Resource,
|
|
28
|
+
normal,
|
|
29
|
+
type ComposeFunction,
|
|
30
|
+
} from "@crossplane-org/function-sdk-typescript";
|
|
31
|
+
|
|
32
|
+
export const compose: ComposeFunction = (req, rsp, logger) => {
|
|
33
|
+
logger?.info("Processing function request");
|
|
34
|
+
|
|
35
|
+
rsp.desired.resources["my-config"] = Resource.fromJSON({
|
|
36
|
+
resource: {
|
|
37
|
+
apiVersion: "v1",
|
|
38
|
+
kind: "ConfigMap",
|
|
39
|
+
metadata: { name: "my-config" },
|
|
40
|
+
data: { key: "value" },
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
normal(rsp, "Function completed successfully");
|
|
45
|
+
return rsp;
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Your entry point hands it to `serve()`, which parses the standard function flags,
|
|
50
|
+
builds a logger, starts the gRPC server and handles shutdown:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
#!/usr/bin/env node
|
|
54
|
+
|
|
55
|
+
import { serve } from "@crossplane-org/function-sdk-typescript";
|
|
56
|
+
import { compose } from "./my-function.js";
|
|
57
|
+
|
|
58
|
+
serve(compose);
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
If your function needs the full interface, implement `FunctionHandler` — `serve()`
|
|
62
|
+
accepts either:
|
|
24
63
|
|
|
25
64
|
```typescript
|
|
26
65
|
import type {
|
|
@@ -625,11 +664,23 @@ import {
|
|
|
625
664
|
|
|
626
665
|
// Runtime types
|
|
627
666
|
ServerOptions,
|
|
667
|
+
ComposeFunction,
|
|
668
|
+
ComposeResponse,
|
|
669
|
+
ServeOptions,
|
|
628
670
|
} from "@crossplane-org/function-sdk-typescript";
|
|
629
671
|
```
|
|
630
672
|
|
|
631
673
|
### Core Functions
|
|
632
674
|
|
|
675
|
+
#### Server Functions
|
|
676
|
+
|
|
677
|
+
- **`serve(fn, opts?)`** - Run a `ComposeFunction` or `FunctionHandler` as a gRPC server
|
|
678
|
+
- **`fromCompose(compose)`** - Adapt a `ComposeFunction` to the `FunctionHandler` interface
|
|
679
|
+
- **`parseArgs(argv)`** - Parse the standard function flags
|
|
680
|
+
- **`helpText(name)`** - The `--help` text for the standard flags
|
|
681
|
+
- **`newGrpcServer(runner, logger)`** - Create a gRPC server instance
|
|
682
|
+
- **`startServer(server, opts, logger)`** - Bind and start the server
|
|
683
|
+
|
|
633
684
|
#### Response Functions
|
|
634
685
|
|
|
635
686
|
- **`to(req, ttl?)`** - Initialize a response from a request
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { advertiseCapabilities, getContextKey, getCredentials, getDesiredCompose
|
|
|
4
4
|
export { DEFAULT_TTL, fatal, normal, requireResource, requireSchema, setContextKey, setDesiredComposedResources, setDesiredCompositeResource, setDesiredCompositeStatus, setDesiredResources, setOutput, to, update, updateDesiredComposedResources, warning, } from './response/response.js';
|
|
5
5
|
export { asObject, asStruct, type Composite, type Condition as ResourceCondition, type ConnectionDetails, type DesiredComposed, fromModel, fromObject, getCondition, type MergeOptions, mustStructJSON, mustStructObject, newDesiredComposed, type ObservedComposed, toObject, update as updateResource, } from './resource/resource.js';
|
|
6
6
|
export { getServerCredentials, newGrpcServer, type ServerOptions, startServer, } from './runtime/runtime.js';
|
|
7
|
+
export { type ComposeFunction, type ComposeResponse, DEFAULT_ADDRESS, DEFAULT_TLS_SERVER_CERTS_DIR, fromCompose, helpText, parseArgs, serve, type ServeOptions, } from './serve/serve.js';
|
|
7
8
|
export { Capability, Condition, CredentialData, Credentials, FunctionRunnerServiceService, Ready, Requirements, Resource, Resources, ResourceSelector, Result, RunFunctionRequest, RunFunctionResponse, Schema, SchemaSelector, Severity, State, Status, Target, } from './proto/run_function.js';
|
|
8
9
|
export type { Logger } from 'pino';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnE,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,2BAA2B,EAC3B,2BAA2B,EAC3B,QAAQ,EACR,4BAA4B,EAC5B,4BAA4B,EAC5B,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,GACd,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,mBAAmB,EACnB,SAAS,EACT,EAAE,EACF,MAAM,EACN,8BAA8B,EAC9B,OAAO,GACR,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,KAAK,SAAS,EACd,KAAK,SAAS,IAAI,iBAAiB,EACnC,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,KAAK,YAAY,EACjB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,QAAQ,EACR,MAAM,IAAI,cAAc,GACzB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,KAAK,aAAa,EAClB,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,UAAU,EACV,SAAS,EACT,cAAc,EACd,WAAW,EACX,4BAA4B,EAC5B,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,kBAAkB,EAClB,mBAAmB,EACnB,MAAM,EACN,cAAc,EACd,QAAQ,EACR,KAAK,EACL,MAAM,EACN,MAAM,GACP,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,YAAY,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnE,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,2BAA2B,EAC3B,2BAA2B,EAC3B,QAAQ,EACR,4BAA4B,EAC5B,4BAA4B,EAC5B,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,GACd,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,mBAAmB,EACnB,SAAS,EACT,EAAE,EACF,MAAM,EACN,8BAA8B,EAC9B,OAAO,GACR,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,KAAK,SAAS,EACd,KAAK,SAAS,IAAI,iBAAiB,EACnC,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,KAAK,YAAY,EACjB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,QAAQ,EACR,MAAM,IAAI,cAAc,GACzB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,KAAK,aAAa,EAClB,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,eAAe,EACf,4BAA4B,EAC5B,WAAW,EACX,QAAQ,EACR,SAAS,EACT,KAAK,EACL,KAAK,YAAY,GAClB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACL,UAAU,EACV,SAAS,EACT,cAAc,EACd,WAAW,EACX,4BAA4B,EAC5B,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,kBAAkB,EAClB,mBAAmB,EACnB,MAAM,EACN,cAAc,EACd,QAAQ,EACR,KAAK,EACL,MAAM,EACN,MAAM,GACP,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,8 @@ export { DEFAULT_TTL, fatal, normal, requireResource, requireSchema, setContextK
|
|
|
8
8
|
export { asObject, asStruct, fromModel, fromObject, getCondition, mustStructJSON, mustStructObject, newDesiredComposed, toObject, update as updateResource, } from './resource/resource.js';
|
|
9
9
|
// Runtime utilities
|
|
10
10
|
export { getServerCredentials, newGrpcServer, startServer, } from './runtime/runtime.js';
|
|
11
|
+
// Entrypoint helpers
|
|
12
|
+
export { DEFAULT_ADDRESS, DEFAULT_TLS_SERVER_CERTS_DIR, fromCompose, helpText, parseArgs, serve, } from './serve/serve.js';
|
|
11
13
|
// Protocol buffer types
|
|
12
14
|
export { Capability, Condition, CredentialData, Credentials, FunctionRunnerServiceService, Ready, Requirements, Resource, Resources, ResourceSelector, Result, RunFunctionRequest, RunFunctionResponse, Schema, SchemaSelector, Severity, State, Status, Target, } from './proto/run_function.js';
|
|
13
15
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAInD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnE,kBAAkB;AAClB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,2BAA2B,EAC3B,2BAA2B,EAC3B,QAAQ,EACR,4BAA4B,EAC5B,4BAA4B,EAC5B,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,GACd,MAAM,sBAAsB,CAAC;AAE9B,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,mBAAmB,EACnB,SAAS,EACT,EAAE,EACF,MAAM,EACN,8BAA8B,EAC9B,OAAO,GACR,MAAM,wBAAwB,CAAC;AAEhC,qBAAqB;AACrB,OAAO,EACL,QAAQ,EACR,QAAQ,EAKR,SAAS,EACT,UAAU,EACV,YAAY,EAEZ,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAElB,QAAQ,EACR,MAAM,IAAI,cAAc,GACzB,MAAM,wBAAwB,CAAC;AAEhC,oBAAoB;AACpB,OAAO,EACL,oBAAoB,EACpB,aAAa,EAEb,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAE9B,wBAAwB;AACxB,OAAO,EACL,UAAU,EACV,SAAS,EACT,cAAc,EACd,WAAW,EACX,4BAA4B,EAC5B,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,kBAAkB,EAClB,mBAAmB,EACnB,MAAM,EACN,cAAc,EACd,QAAQ,EACR,KAAK,EACL,MAAM,EACN,MAAM,GACP,MAAM,yBAAyB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AAInD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAEnE,kBAAkB;AAClB,OAAO,EACL,qBAAqB,EACrB,aAAa,EACb,cAAc,EACd,2BAA2B,EAC3B,2BAA2B,EAC3B,QAAQ,EACR,4BAA4B,EAC5B,4BAA4B,EAC5B,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,aAAa,GACd,MAAM,sBAAsB,CAAC;AAE9B,mBAAmB;AACnB,OAAO,EACL,WAAW,EACX,KAAK,EACL,MAAM,EACN,eAAe,EACf,aAAa,EACb,aAAa,EACb,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,mBAAmB,EACnB,SAAS,EACT,EAAE,EACF,MAAM,EACN,8BAA8B,EAC9B,OAAO,GACR,MAAM,wBAAwB,CAAC;AAEhC,qBAAqB;AACrB,OAAO,EACL,QAAQ,EACR,QAAQ,EAKR,SAAS,EACT,UAAU,EACV,YAAY,EAEZ,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAElB,QAAQ,EACR,MAAM,IAAI,cAAc,GACzB,MAAM,wBAAwB,CAAC;AAEhC,oBAAoB;AACpB,OAAO,EACL,oBAAoB,EACpB,aAAa,EAEb,WAAW,GACZ,MAAM,sBAAsB,CAAC;AAE9B,qBAAqB;AACrB,OAAO,EAGL,eAAe,EACf,4BAA4B,EAC5B,WAAW,EACX,QAAQ,EACR,SAAS,EACT,KAAK,GAEN,MAAM,kBAAkB,CAAC;AAE1B,wBAAwB;AACxB,OAAO,EACL,UAAU,EACV,SAAS,EACT,cAAc,EACd,WAAW,EACX,4BAA4B,EAC5B,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,kBAAkB,EAClB,mBAAmB,EACnB,MAAM,EACN,cAAc,EACd,QAAQ,EACR,KAAK,EACL,MAAM,EACN,MAAM,GACP,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A batteries-included entrypoint for composition functions.
|
|
3
|
+
*
|
|
4
|
+
* Writing a function should not require assembling a gRPC server. This module
|
|
5
|
+
* provides {@link serve}, which parses the standard function flags, builds a
|
|
6
|
+
* logger, starts the server and handles shutdown — so a function's entrypoint
|
|
7
|
+
* is a single call, and its author only writes composition logic.
|
|
8
|
+
*
|
|
9
|
+
* It also provides the {@link ComposeFunction} shape: a plain function handed
|
|
10
|
+
* a request and a response to populate, rather than a class implementing
|
|
11
|
+
* {@link FunctionHandler}. Both are accepted, so existing handlers keep
|
|
12
|
+
* working.
|
|
13
|
+
*/
|
|
14
|
+
import * as grpc from '@grpc/grpc-js';
|
|
15
|
+
import { type Logger } from 'pino';
|
|
16
|
+
import { type FunctionHandler } from '../function/function.js';
|
|
17
|
+
import { type ServerOptions } from '../runtime/runtime.js';
|
|
18
|
+
import type { RunFunctionRequest, RunFunctionResponse, State } from '../proto/run_function.js';
|
|
19
|
+
/** The address a function listens on unless told otherwise. */
|
|
20
|
+
export declare const DEFAULT_ADDRESS = "0.0.0.0:9443";
|
|
21
|
+
/**
|
|
22
|
+
* Where the package reconciler mounts the TLS certificates it generates.
|
|
23
|
+
*/
|
|
24
|
+
export declare const DEFAULT_TLS_SERVER_CERTS_DIR = "/tls/server";
|
|
25
|
+
/**
|
|
26
|
+
* A RunFunctionResponse whose desired state is guaranteed to be present.
|
|
27
|
+
*
|
|
28
|
+
* `RunFunctionResponse.desired` is optional in the protobuf schema, but a
|
|
29
|
+
* response built by {@link to} always has it, so a compose function can write
|
|
30
|
+
* `rsp.desired.resources[name]` without a non-null assertion.
|
|
31
|
+
*/
|
|
32
|
+
export type ComposeResponse = RunFunctionResponse & {
|
|
33
|
+
desired: State;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* A composition function.
|
|
37
|
+
*
|
|
38
|
+
* Receives the request along with a response already initialised from it, so
|
|
39
|
+
* there is no need to call {@link to}, and returns the response to send.
|
|
40
|
+
*
|
|
41
|
+
* The response is passed in as a convenience, not as an out parameter: it is
|
|
42
|
+
* yours to fill in and return, or to ignore in favour of one you build
|
|
43
|
+
* yourself. Returning it is required, so forgetting is a compile error rather
|
|
44
|
+
* than an empty response at runtime.
|
|
45
|
+
*
|
|
46
|
+
* Note that when the request already carries desired state — as it does for
|
|
47
|
+
* every function after the first in a pipeline — `rsp.desired` is the same
|
|
48
|
+
* object as `req.desired`, not a copy. Writing to `rsp.desired.resources`
|
|
49
|
+
* therefore also changes `req.desired.resources`. That is inherited from
|
|
50
|
+
* {@link to} and is usually harmless, since a function reads observed state
|
|
51
|
+
* and writes desired state, but do not rely on `req.desired` still holding
|
|
52
|
+
* what the previous function left once you have started writing.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* import { Resource, type ComposeFunction } from '@crossplane-org/function-sdk-typescript';
|
|
57
|
+
* import { VPC } from 'crossplane-models/ec2.aws.m.upbound.io/v1beta1';
|
|
58
|
+
*
|
|
59
|
+
* export const compose: ComposeFunction = (req, rsp) => {
|
|
60
|
+
* const vpc = new VPC({ spec: { forProvider: { region: 'us-west-2' } } });
|
|
61
|
+
* vpc.validate();
|
|
62
|
+
* rsp.desired.resources['vpc'] = Resource.fromJSON({ resource: vpc.toJSON() });
|
|
63
|
+
* return rsp;
|
|
64
|
+
* };
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export type ComposeFunction = (req: RunFunctionRequest, rsp: ComposeResponse, logger?: Logger) => RunFunctionResponse | Promise<RunFunctionResponse>;
|
|
68
|
+
/**
|
|
69
|
+
* Adapt a {@link ComposeFunction} to the {@link FunctionHandler} interface.
|
|
70
|
+
*
|
|
71
|
+
* Builds the response from the request, hands it to the compose function, and
|
|
72
|
+
* returns whatever that function returns.
|
|
73
|
+
*/
|
|
74
|
+
export declare function fromCompose(compose: ComposeFunction): FunctionHandler;
|
|
75
|
+
/** Options for {@link serve}. */
|
|
76
|
+
export interface ServeOptions {
|
|
77
|
+
/**
|
|
78
|
+
* Program name shown in --help. Defaults to the basename of the running
|
|
79
|
+
* script.
|
|
80
|
+
*/
|
|
81
|
+
name?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Arguments to parse, without the node executable or script path.
|
|
84
|
+
* Defaults to process.argv.slice(2).
|
|
85
|
+
*/
|
|
86
|
+
argv?: string[];
|
|
87
|
+
/**
|
|
88
|
+
* Logger to use. One is created from the parsed --debug flag if omitted.
|
|
89
|
+
*/
|
|
90
|
+
logger?: Logger;
|
|
91
|
+
/** Overrides applied on top of the parsed flags. */
|
|
92
|
+
serverOptions?: Partial<ServerOptions>;
|
|
93
|
+
}
|
|
94
|
+
/** The result of {@link parseArgs}: server options, plus whether help was asked for. */
|
|
95
|
+
export interface ParsedArgs extends ServerOptions {
|
|
96
|
+
/** Whether --help or -h was given. */
|
|
97
|
+
help: boolean;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Parse the standard function command line flags.
|
|
101
|
+
*
|
|
102
|
+
* Exported so that it can be tested, and so that a function needing extra
|
|
103
|
+
* flags of its own can reuse the standard ones.
|
|
104
|
+
*
|
|
105
|
+
* @param argv - Arguments without the node executable or script path.
|
|
106
|
+
* @returns The parsed options.
|
|
107
|
+
* @throws If a flag is unrecognised, missing its value, or given a value it
|
|
108
|
+
* does not take.
|
|
109
|
+
*/
|
|
110
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
111
|
+
/** The --help text, derived from {@link flags} and {@link descriptions}. */
|
|
112
|
+
export declare function helpText(name: string): string;
|
|
113
|
+
/**
|
|
114
|
+
* The message shown when the command line cannot be parsed.
|
|
115
|
+
*
|
|
116
|
+
* Kept separate from {@link serve} so that it can be tested without spawning a
|
|
117
|
+
* process, since serve's own handling ends in process.exit.
|
|
118
|
+
*/
|
|
119
|
+
export declare function usageErrorText(name: string, error: unknown): string;
|
|
120
|
+
/**
|
|
121
|
+
* Run a composition function as a gRPC server.
|
|
122
|
+
*
|
|
123
|
+
* Parses the standard flags, creates a logger, starts the server, and shuts it
|
|
124
|
+
* down cleanly on SIGINT and SIGTERM. This is everything a function's
|
|
125
|
+
* entrypoint needs to do.
|
|
126
|
+
*
|
|
127
|
+
* @param fn - A {@link ComposeFunction}, or a {@link FunctionHandler} for
|
|
128
|
+
* functions that need the full interface.
|
|
129
|
+
* @param opts - Overrides, mostly useful in tests.
|
|
130
|
+
* @returns The running gRPC server.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* #!/usr/bin/env node
|
|
135
|
+
* import { serve } from '@crossplane-org/function-sdk-typescript';
|
|
136
|
+
* import { compose } from './function.js';
|
|
137
|
+
*
|
|
138
|
+
* serve(compose);
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
export declare function serve(fn: ComposeFunction | FunctionHandler, opts?: ServeOptions): grpc.Server;
|
|
142
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/serve/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,IAAI,MAAM,eAAe,CAAC;AAGtC,OAAO,EAAQ,KAAK,MAAM,EAAE,MAAM,MAAM,CAAC;AACzC,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/E,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAE/F,+DAA+D;AAC/D,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,4BAA4B,gBAAgB,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,mBAAmB,GAAG;IAAE,OAAO,EAAE,KAAK,CAAA;CAAE,CAAC;AAcvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,eAAe,GAAG,CAC5B,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,eAAe,EACpB,MAAM,CAAC,EAAE,MAAM,KACZ,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAExD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,eAAe,CAQrE;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,oDAAoD;IACpD,aAAa,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CACxC;AA6BD,wFAAwF;AACxF,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,sCAAsC;IACtC,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CASpD;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAc7C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAGnE;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,eAAe,GAAG,eAAe,EAAE,IAAI,GAAE,YAAiB,GAAG,IAAI,CAAC,MAAM,CAqDjG"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A batteries-included entrypoint for composition functions.
|
|
3
|
+
*
|
|
4
|
+
* Writing a function should not require assembling a gRPC server. This module
|
|
5
|
+
* provides {@link serve}, which parses the standard function flags, builds a
|
|
6
|
+
* logger, starts the server and handles shutdown — so a function's entrypoint
|
|
7
|
+
* is a single call, and its author only writes composition logic.
|
|
8
|
+
*
|
|
9
|
+
* It also provides the {@link ComposeFunction} shape: a plain function handed
|
|
10
|
+
* a request and a response to populate, rather than a class implementing
|
|
11
|
+
* {@link FunctionHandler}. Both are accepted, so existing handlers keep
|
|
12
|
+
* working.
|
|
13
|
+
*/
|
|
14
|
+
import * as grpc from '@grpc/grpc-js';
|
|
15
|
+
import { basename } from 'node:path';
|
|
16
|
+
import { parseArgs as parseNodeArgs } from 'node:util';
|
|
17
|
+
import { pino } from 'pino';
|
|
18
|
+
import { FunctionRunner } from '../function/function.js';
|
|
19
|
+
import { newGrpcServer, startServer } from '../runtime/runtime.js';
|
|
20
|
+
import { to } from '../response/response.js';
|
|
21
|
+
/** The address a function listens on unless told otherwise. */
|
|
22
|
+
export const DEFAULT_ADDRESS = '0.0.0.0:9443';
|
|
23
|
+
/**
|
|
24
|
+
* Where the package reconciler mounts the TLS certificates it generates.
|
|
25
|
+
*/
|
|
26
|
+
export const DEFAULT_TLS_SERVER_CERTS_DIR = '/tls/server';
|
|
27
|
+
/**
|
|
28
|
+
* The program name to show in --help when the caller does not supply one.
|
|
29
|
+
*
|
|
30
|
+
* Taken from the script being run, the way most command line tools do it, so
|
|
31
|
+
* the help of a function started as `node dist/main.js` says `main.js` rather
|
|
32
|
+
* than something generic.
|
|
33
|
+
*/
|
|
34
|
+
function defaultName() {
|
|
35
|
+
const script = process.argv[1];
|
|
36
|
+
return script ? basename(script) : 'function';
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Adapt a {@link ComposeFunction} to the {@link FunctionHandler} interface.
|
|
40
|
+
*
|
|
41
|
+
* Builds the response from the request, hands it to the compose function, and
|
|
42
|
+
* returns whatever that function returns.
|
|
43
|
+
*/
|
|
44
|
+
export function fromCompose(compose) {
|
|
45
|
+
return {
|
|
46
|
+
async RunFunction(req, logger) {
|
|
47
|
+
// to() always populates desired, so the cast holds.
|
|
48
|
+
const rsp = to(req);
|
|
49
|
+
return await compose(req, rsp, logger);
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The flags every composition function accepts.
|
|
55
|
+
*
|
|
56
|
+
* This is the single source of truth: both the parser and the help text are
|
|
57
|
+
* derived from it, so a flag cannot be added to one and forgotten in the
|
|
58
|
+
* other.
|
|
59
|
+
*/
|
|
60
|
+
const flags = {
|
|
61
|
+
address: { type: 'string', default: DEFAULT_ADDRESS },
|
|
62
|
+
debug: { type: 'boolean', short: 'd', default: false },
|
|
63
|
+
insecure: { type: 'boolean', default: false },
|
|
64
|
+
'tls-server-certs-dir': { type: 'string', default: DEFAULT_TLS_SERVER_CERTS_DIR },
|
|
65
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* What each flag does. Keyed by {@link flags}, so adding a flag without
|
|
69
|
+
* describing it is a compile error.
|
|
70
|
+
*/
|
|
71
|
+
const descriptions = {
|
|
72
|
+
address: `Address to listen for gRPC connections. Default ${DEFAULT_ADDRESS}.`,
|
|
73
|
+
debug: 'Emit debug logs.',
|
|
74
|
+
insecure: 'Run without mTLS credentials.',
|
|
75
|
+
'tls-server-certs-dir': `Directory holding tls.key, tls.crt and ca.crt. Default ${DEFAULT_TLS_SERVER_CERTS_DIR}.`,
|
|
76
|
+
help: 'Show this help.',
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Parse the standard function command line flags.
|
|
80
|
+
*
|
|
81
|
+
* Exported so that it can be tested, and so that a function needing extra
|
|
82
|
+
* flags of its own can reuse the standard ones.
|
|
83
|
+
*
|
|
84
|
+
* @param argv - Arguments without the node executable or script path.
|
|
85
|
+
* @returns The parsed options.
|
|
86
|
+
* @throws If a flag is unrecognised, missing its value, or given a value it
|
|
87
|
+
* does not take.
|
|
88
|
+
*/
|
|
89
|
+
export function parseArgs(argv) {
|
|
90
|
+
const { values } = parseNodeArgs({ args: argv, options: flags, allowPositionals: false });
|
|
91
|
+
return {
|
|
92
|
+
address: values.address,
|
|
93
|
+
debug: values.debug,
|
|
94
|
+
insecure: values.insecure,
|
|
95
|
+
tlsServerCertsDir: values['tls-server-certs-dir'],
|
|
96
|
+
help: values.help,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/** The --help text, derived from {@link flags} and {@link descriptions}. */
|
|
100
|
+
export function helpText(name) {
|
|
101
|
+
const usage = Object.entries(flags).map(([flag, spec]) => {
|
|
102
|
+
const short = 'short' in spec ? `-${spec.short}, ` : ' ';
|
|
103
|
+
const value = spec.type === 'string' ? ' <value>' : '';
|
|
104
|
+
return ` ${short}--${flag}${value}`.padEnd(38) + descriptions[flag];
|
|
105
|
+
});
|
|
106
|
+
return [
|
|
107
|
+
`Usage: ${name} [flags]`,
|
|
108
|
+
'',
|
|
109
|
+
'A Crossplane composition function.',
|
|
110
|
+
'',
|
|
111
|
+
'Flags:',
|
|
112
|
+
...usage,
|
|
113
|
+
].join('\n');
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The message shown when the command line cannot be parsed.
|
|
117
|
+
*
|
|
118
|
+
* Kept separate from {@link serve} so that it can be tested without spawning a
|
|
119
|
+
* process, since serve's own handling ends in process.exit.
|
|
120
|
+
*/
|
|
121
|
+
export function usageErrorText(name, error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
return `${name}: ${message}\nTry '${name} --help' for the available flags.`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Run a composition function as a gRPC server.
|
|
127
|
+
*
|
|
128
|
+
* Parses the standard flags, creates a logger, starts the server, and shuts it
|
|
129
|
+
* down cleanly on SIGINT and SIGTERM. This is everything a function's
|
|
130
|
+
* entrypoint needs to do.
|
|
131
|
+
*
|
|
132
|
+
* @param fn - A {@link ComposeFunction}, or a {@link FunctionHandler} for
|
|
133
|
+
* functions that need the full interface.
|
|
134
|
+
* @param opts - Overrides, mostly useful in tests.
|
|
135
|
+
* @returns The running gRPC server.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* #!/usr/bin/env node
|
|
140
|
+
* import { serve } from '@crossplane-org/function-sdk-typescript';
|
|
141
|
+
* import { compose } from './function.js';
|
|
142
|
+
*
|
|
143
|
+
* serve(compose);
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
export function serve(fn, opts = {}) {
|
|
147
|
+
const name = opts.name ?? defaultName();
|
|
148
|
+
const argv = opts.argv ?? process.argv.slice(2);
|
|
149
|
+
let args;
|
|
150
|
+
try {
|
|
151
|
+
args = parseArgs(argv);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// node:util throws a TypeError whose message is exactly what the user
|
|
155
|
+
// needs to see. Without this it reaches the top level and Node prints it
|
|
156
|
+
// under a stack trace through its own internals, which buries it.
|
|
157
|
+
process.stderr.write(`${usageErrorText(name, error)}\n`);
|
|
158
|
+
process.exit(2);
|
|
159
|
+
}
|
|
160
|
+
const { help, ...parsed } = args;
|
|
161
|
+
if (help) {
|
|
162
|
+
process.stdout.write(`${helpText(name)}\n`);
|
|
163
|
+
process.exit(0);
|
|
164
|
+
}
|
|
165
|
+
const serverOptions = { ...parsed, ...opts.serverOptions };
|
|
166
|
+
const logger = opts.logger ??
|
|
167
|
+
pino({
|
|
168
|
+
level: serverOptions.debug ? 'debug' : 'info',
|
|
169
|
+
formatters: {
|
|
170
|
+
level: (label) => ({ severity: label.toUpperCase() }),
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
logger.debug({ options: serverOptions }, 'starting function');
|
|
174
|
+
const handler = typeof fn === 'function' ? fromCompose(fn) : fn;
|
|
175
|
+
const server = newGrpcServer(new FunctionRunner(handler, logger), logger);
|
|
176
|
+
startServer(server, serverOptions, logger);
|
|
177
|
+
const shutdown = (signal) => {
|
|
178
|
+
logger.info(`received ${signal}, shutting down`);
|
|
179
|
+
server.tryShutdown((err) => {
|
|
180
|
+
if (err) {
|
|
181
|
+
logger.error(err, 'error during shutdown');
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
logger.info('server shut down');
|
|
185
|
+
process.exit(0);
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
189
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
190
|
+
return server;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../../src/serve/serve.ts"],"names":[],"mappings":"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"}
|