@sequenceholdings/managed-functions 0.1.1 → 0.3.0
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 +135 -6
- package/dist/capabilities.d.ts +112 -0
- package/dist/capabilities.js +99 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +14 -1
- package/dist/deployed-policy.d.ts +2 -0
- package/dist/deployed-policy.js +88 -0
- package/dist/limits.d.ts +6 -3
- package/dist/limits.js +10 -3
- package/dist/orm.d.ts +65 -0
- package/dist/orm.js +297 -0
- package/dist/preflight.d.ts +9 -0
- package/dist/preflight.js +172 -0
- package/dist/runtime.cjs +626 -9
- package/dist/sdk-range.d.ts +2 -0
- package/dist/sdk-range.js +146 -0
- package/dist/storage.d.ts +76 -0
- package/package.json +16 -3
package/README.md
CHANGED
|
@@ -1,15 +1,144 @@
|
|
|
1
1
|
# `@sequenceholdings/managed-functions`
|
|
2
2
|
|
|
3
|
-
`@sequenceholdings/managed-functions` is the TypeScript SDK for
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
`@sequenceholdings/managed-functions` is the TypeScript SDK for Managed
|
|
4
|
+
Functions. It provides caller-scoped ORM access, explicit service-account ORM
|
|
5
|
+
access, and opt-in caching. The
|
|
6
|
+
`/orm` import loads only the dependency-free ORM execution client, without
|
|
7
|
+
initializing cache, storage, authentication or tracing libraries.
|
|
7
8
|
|
|
8
9
|
Managed Functions is a workflow-agnostic platform primitive. The runtime owns
|
|
9
10
|
generic invocation, isolation, secrets, egress, caching, and observability;
|
|
10
11
|
deployed functions own business logic. Primitive code must not identify a
|
|
11
|
-
function consumer or branch on a function ID.
|
|
12
|
-
|
|
12
|
+
function consumer or branch on a function ID.
|
|
13
|
+
|
|
14
|
+
## ORM as the invoking user
|
|
15
|
+
|
|
16
|
+
Available in SDK **0.2.0 and later**. Install the SDK in the function package;
|
|
17
|
+
the separate ORM authoring/compiler package is not needed in the deployed
|
|
18
|
+
function. Copy the namespace's generated `operations.manifest.json` into that
|
|
19
|
+
package, and keep it synchronized with the applied namespace version.
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import type { Request, Response } from '@google-cloud/functions-framework'
|
|
23
|
+
import { createFunctionOrmExecutor, FunctionOrmError } from '@sequenceholdings/managed-functions/orm'
|
|
24
|
+
import manifest from './operations.manifest.json'
|
|
25
|
+
|
|
26
|
+
// Nonsecret configuration: use your deployment's URL, never request input.
|
|
27
|
+
const platformOrigin = 'https://your-environment.example.com'
|
|
28
|
+
|
|
29
|
+
export async function handler(request: Request, response: Response) {
|
|
30
|
+
try {
|
|
31
|
+
// Create inside every invocation; never keep this executor in module state.
|
|
32
|
+
const orm = createFunctionOrmExecutor({
|
|
33
|
+
request,
|
|
34
|
+
baseUrl: platformOrigin,
|
|
35
|
+
namespace: 'directory',
|
|
36
|
+
manifest,
|
|
37
|
+
})
|
|
38
|
+
const profile = await orm.execute('GetMyProfile', {})
|
|
39
|
+
response.json(profile)
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (error instanceof FunctionOrmError) {
|
|
42
|
+
response.status(403).json({ error: error.code })
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
// Do not expose arbitrary exceptions or upstream response bodies.
|
|
46
|
+
response.status(502).json({ error: 'PROFILE_UNAVAILABLE' })
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The manifest declares the account and data scope. Replace these illustrative
|
|
52
|
+
names with your provisioned account, namespace, relations and mutation names:
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
service_account: directory-runtime
|
|
56
|
+
capabilities:
|
|
57
|
+
data:
|
|
58
|
+
directory:
|
|
59
|
+
tables: [my_profile]
|
|
60
|
+
operations: [UpdateMyProfile]
|
|
61
|
+
query: false
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The deployer needs permission to bind the service account. The human caller
|
|
65
|
+
needs function invocation access plus the namespace runtime grants and operation
|
|
66
|
+
capabilities. `tables` covers read relations, including linked selections and
|
|
67
|
+
policy dependencies; `operations` contains persisted mutation names. These
|
|
68
|
+
declarations restrict access; they do not grant the caller access. Apply matching
|
|
69
|
+
namespace operations before deploying consumers. Open the Managed Functions guide at `/docs/managed-functions/overview` on
|
|
70
|
+
your administrator-supplied platform URL for provisioning, grants and deployment.
|
|
71
|
+
|
|
72
|
+
`createFunctionOrmExecutor({ request, baseUrl, namespace, manifest, fetchImpl? })`
|
|
73
|
+
returns an isolated executor with `execute(operationName, variables?)`. It sends
|
|
74
|
+
only registered operation hashes and variables. The optional `fetchImpl` is for
|
|
75
|
+
testing or an application-owned transport; it must honor `redirect: 'error'`.
|
|
76
|
+
It never retries requests, follows redirects, reads credentials from a request
|
|
77
|
+
body, substitutes a service token, or configures process-wide ORM state. Reuse
|
|
78
|
+
within one handler invocation is supported; discard it when the handler returns.
|
|
79
|
+
|
|
80
|
+
By default `numeric` and `bigint` values come back as JSON numbers, which
|
|
81
|
+
JavaScript rounds past 15-17 significant digits. Pass
|
|
82
|
+
`jsonScalars: 'lossless-v1'` to receive them as strings (`"250000.00"`); the
|
|
83
|
+
executor then refuses any successful result the platform does not acknowledge
|
|
84
|
+
in that form, with `OrmOperationError` code `LOSSLESS_TRANSPORT_UNAVAILABLE`.
|
|
85
|
+
Send `numeric` and `bigint` variables as decimal strings in either mode.
|
|
86
|
+
|
|
87
|
+
`baseUrl` must be an explicitly configured HTTPS origin, with no path, query,
|
|
88
|
+
fragment or embedded credentials. Loopback HTTP is accepted for a local platform.
|
|
89
|
+
There is no automatic environment URL: the helper never trusts a request header
|
|
90
|
+
or body to choose where it sends a delegated credential.
|
|
91
|
+
|
|
92
|
+
### Caller identity and trust boundary
|
|
93
|
+
|
|
94
|
+
This helper supports direct human invocations through the platform gateway.
|
|
95
|
+
Scheduled/system-runner and service-account invocations fail closed. They do not
|
|
96
|
+
silently become an attached service account or a recorded human initiator.
|
|
97
|
+
|
|
98
|
+
`createFunctionServiceOrmExecutor` takes the same options and executes as the
|
|
99
|
+
function's attached `service_account` instead. Use it for invocations with no
|
|
100
|
+
human caller, such as a Lattice managed-function step, or where the function must
|
|
101
|
+
deliberately act beyond its caller. The account's grants, capabilities and row
|
|
102
|
+
policies apply, not the caller's, so enforce any per-user rule in the handler.
|
|
103
|
+
The person who started the invocation stays on the credential for audit.
|
|
104
|
+
No Function UUID, profile lookup, signing key, bearer token or claims decoder is
|
|
105
|
+
required in application code.
|
|
106
|
+
|
|
107
|
+
The platform must control ingress to the deployed handler. The helper consumes
|
|
108
|
+
the request delivered by that gateway; it does **not** authenticate arbitrary
|
|
109
|
+
HTTP requests or make a publicly reachable Functions Framework server safe.
|
|
110
|
+
Its local metadata checks only reject an unsupported or expired invocation mode.
|
|
111
|
+
The ORM server verifies the credential's signature and expiry, checks its
|
|
112
|
+
declared scope, resolves the caller's current permissions, and enforces row
|
|
113
|
+
policies on every operation. The helper exposes no decoded caller identity or
|
|
114
|
+
capabilities for use as a separate authorization decision.
|
|
115
|
+
|
|
116
|
+
`seq-studio functions dev` does not mint delegated user identity. A missing
|
|
117
|
+
context there is expected; validate pure handler logic with supplied test
|
|
118
|
+
executors and verify real caller permissions through a deployed invocation.
|
|
119
|
+
Do not copy real invocation headers into fixtures or logs.
|
|
120
|
+
|
|
121
|
+
### Errors and recovery
|
|
122
|
+
|
|
123
|
+
`FunctionOrmError.code` is stable and safe to return without including the
|
|
124
|
+
exception's stack or request headers:
|
|
125
|
+
|
|
126
|
+
| Code | Recovery |
|
|
127
|
+
| --- | --- |
|
|
128
|
+
| `INVALID_CONFIGURATION` | Correct the trusted origin or fetch configuration. |
|
|
129
|
+
| `MISSING_CALLER_CONTEXT` | Declare the data binding. Caller mode: invoke through the platform as a signed-in user. Service mode: also declare `service_account` and invoke through the platform. |
|
|
130
|
+
| `INVALID_CALLER_CONTEXT` | Verify that the request came through the supported platform runtime. |
|
|
131
|
+
| `UNSUPPORTED_CALLER` | The invocation mode doesn't match the helper: use `createFunctionOrmExecutor` for a direct human invocation, and `createFunctionServiceOrmExecutor` for a system-runner (Lattice) invocation. |
|
|
132
|
+
| `EXPIRED_CALLER_CONTEXT` | Begin a new invocation and construct a new executor. |
|
|
133
|
+
|
|
134
|
+
Invalid manifests, unknown operations and ORM failures propagate `OrmClientError`
|
|
135
|
+
or `OrmOperationError`, also exported from `/orm`. An `OrmOperationError` exposes
|
|
136
|
+
`status` and `code`; handle those instead of returning its upstream message.
|
|
137
|
+
A denied write is still denied; the helper supplies no permission
|
|
138
|
+
fallback. A network error may follow a successful write: use application-level
|
|
139
|
+
idempotency and inspect persisted results before retrying.
|
|
140
|
+
|
|
141
|
+
## Opt-in cache
|
|
13
142
|
|
|
14
143
|
Opt-in caching inside a Managed Function. When the manifest declares a `cache` block, the platform passes a process-reused `context` as the HTTP handler's third argument. Existing two-argument handlers keep working.
|
|
15
144
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { ManagedFunctionLimitCatalog } from './limits';
|
|
2
|
+
export declare const MANAGED_FUNCTION_CLI_PACKAGE = "@sequenceholdings/studio-cli";
|
|
3
|
+
export declare const MANAGED_FUNCTION_SDK_PACKAGE = "@sequenceholdings/managed-functions";
|
|
4
|
+
export declare const MANAGED_FUNCTION_CAPABILITIES: {
|
|
5
|
+
readonly tooling: {
|
|
6
|
+
readonly cli: {
|
|
7
|
+
readonly package: "@sequenceholdings/studio-cli";
|
|
8
|
+
readonly min: "0.4.0";
|
|
9
|
+
};
|
|
10
|
+
readonly sdk: {
|
|
11
|
+
readonly package: "@sequenceholdings/managed-functions";
|
|
12
|
+
readonly min: "0.3.0";
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
readonly features: {
|
|
16
|
+
readonly postgres_egress: {
|
|
17
|
+
readonly field: "egress";
|
|
18
|
+
readonly supported: true;
|
|
19
|
+
readonly limit: "a credentials-free postgres:// or postgresql:// URL on port 5432 with sslmode=verify-full";
|
|
20
|
+
readonly min_cli: "0.4.0";
|
|
21
|
+
readonly min_sdk: "0.3.0";
|
|
22
|
+
};
|
|
23
|
+
readonly http_headers: {
|
|
24
|
+
readonly field: "invocation.headers";
|
|
25
|
+
readonly max_bytes: number;
|
|
26
|
+
readonly limit: "32768 bytes (16384 bytes is Node's default and returns HTTP 431)";
|
|
27
|
+
readonly min_cli: "0.4.0";
|
|
28
|
+
readonly min_sdk: "0.3.0";
|
|
29
|
+
};
|
|
30
|
+
readonly invoke_timeout: {
|
|
31
|
+
readonly field: "limits.timeout_seconds";
|
|
32
|
+
readonly max_seconds: 3600;
|
|
33
|
+
readonly legacy_sdk_max_seconds: 300;
|
|
34
|
+
readonly limit: "1 to 3600 seconds; SDK clients older than the minimum stop at 300 seconds";
|
|
35
|
+
readonly min_cli: "0.4.0";
|
|
36
|
+
readonly min_sdk: "0.3.0";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
export interface CapabilityTooling {
|
|
41
|
+
cli: {
|
|
42
|
+
package: string;
|
|
43
|
+
min: string;
|
|
44
|
+
};
|
|
45
|
+
sdk: {
|
|
46
|
+
package: string;
|
|
47
|
+
min: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface ManagedFunctionCapabilities {
|
|
51
|
+
tooling: CapabilityTooling;
|
|
52
|
+
features: {
|
|
53
|
+
postgres_egress?: {
|
|
54
|
+
field: string;
|
|
55
|
+
supported: boolean;
|
|
56
|
+
limit: string;
|
|
57
|
+
min_cli: string;
|
|
58
|
+
min_sdk: string;
|
|
59
|
+
};
|
|
60
|
+
http_headers?: {
|
|
61
|
+
field: string;
|
|
62
|
+
max_bytes: number;
|
|
63
|
+
limit: string;
|
|
64
|
+
min_cli: string;
|
|
65
|
+
min_sdk: string;
|
|
66
|
+
};
|
|
67
|
+
invoke_timeout?: {
|
|
68
|
+
field: string;
|
|
69
|
+
max_seconds: number;
|
|
70
|
+
legacy_sdk_max_seconds: number;
|
|
71
|
+
limit: string;
|
|
72
|
+
min_cli: string;
|
|
73
|
+
min_sdk: string;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export interface DeployedPolicy {
|
|
78
|
+
limits: ManagedFunctionLimitCatalog | null;
|
|
79
|
+
capabilities: ManagedFunctionCapabilities | null;
|
|
80
|
+
}
|
|
81
|
+
export interface PreflightManifest {
|
|
82
|
+
limits?: {
|
|
83
|
+
memory_mb?: number;
|
|
84
|
+
cpu?: number | 'auto';
|
|
85
|
+
concurrency?: number;
|
|
86
|
+
timeout_seconds?: number;
|
|
87
|
+
max_response_bytes?: number;
|
|
88
|
+
min_instances?: number;
|
|
89
|
+
max_instances?: number;
|
|
90
|
+
invoke_rate_per_minute?: number;
|
|
91
|
+
};
|
|
92
|
+
egress?: readonly string[];
|
|
93
|
+
capabilities?: {
|
|
94
|
+
data?: Readonly<Record<string, unknown>>;
|
|
95
|
+
storage?: unknown;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export interface PreflightIssue {
|
|
99
|
+
field: string;
|
|
100
|
+
limit: string;
|
|
101
|
+
requiredCli: string;
|
|
102
|
+
requiredSdk: string;
|
|
103
|
+
message: string;
|
|
104
|
+
}
|
|
105
|
+
export declare function versionIsOlder(installed: string, required: string): boolean;
|
|
106
|
+
export declare function sdkVersionLowerBound(spec: string | undefined): string | null;
|
|
107
|
+
export declare function sdkVersionFromPackageJson(raw: string): string | null;
|
|
108
|
+
export declare function isPostgresEgress(entry: string): boolean;
|
|
109
|
+
export declare function formatValidationDetail(issues: readonly {
|
|
110
|
+
path?: readonly PropertyKey[];
|
|
111
|
+
message?: string;
|
|
112
|
+
}[]): string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MANAGED_FUNCTION_CAPABILITIES = exports.MANAGED_FUNCTION_SDK_PACKAGE = exports.MANAGED_FUNCTION_CLI_PACKAGE = void 0;
|
|
4
|
+
exports.versionIsOlder = versionIsOlder;
|
|
5
|
+
exports.sdkVersionLowerBound = sdkVersionLowerBound;
|
|
6
|
+
exports.sdkVersionFromPackageJson = sdkVersionFromPackageJson;
|
|
7
|
+
exports.isPostgresEgress = isPostgresEgress;
|
|
8
|
+
exports.formatValidationDetail = formatValidationDetail;
|
|
9
|
+
const sdk_range_1 = require("./sdk-range");
|
|
10
|
+
exports.MANAGED_FUNCTION_CLI_PACKAGE = '@sequenceholdings/studio-cli';
|
|
11
|
+
exports.MANAGED_FUNCTION_SDK_PACKAGE = '@sequenceholdings/managed-functions';
|
|
12
|
+
exports.MANAGED_FUNCTION_CAPABILITIES = {
|
|
13
|
+
tooling: {
|
|
14
|
+
cli: { package: exports.MANAGED_FUNCTION_CLI_PACKAGE, min: '0.4.0' },
|
|
15
|
+
sdk: { package: exports.MANAGED_FUNCTION_SDK_PACKAGE, min: '0.3.0' },
|
|
16
|
+
},
|
|
17
|
+
features: {
|
|
18
|
+
postgres_egress: {
|
|
19
|
+
field: 'egress',
|
|
20
|
+
supported: true,
|
|
21
|
+
limit: 'a credentials-free postgres:// or postgresql:// URL on port 5432 with sslmode=verify-full',
|
|
22
|
+
min_cli: '0.4.0',
|
|
23
|
+
min_sdk: '0.3.0',
|
|
24
|
+
},
|
|
25
|
+
http_headers: {
|
|
26
|
+
field: 'invocation.headers',
|
|
27
|
+
max_bytes: 32 * 1024,
|
|
28
|
+
limit: '32768 bytes (16384 bytes is Node\'s default and returns HTTP 431)',
|
|
29
|
+
min_cli: '0.4.0',
|
|
30
|
+
min_sdk: '0.3.0',
|
|
31
|
+
},
|
|
32
|
+
invoke_timeout: {
|
|
33
|
+
field: 'limits.timeout_seconds',
|
|
34
|
+
max_seconds: 3600,
|
|
35
|
+
legacy_sdk_max_seconds: 300,
|
|
36
|
+
limit: '1 to 3600 seconds; SDK clients older than the minimum stop at 300 seconds',
|
|
37
|
+
min_cli: '0.4.0',
|
|
38
|
+
min_sdk: '0.3.0',
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
function versionIsOlder(installed, required) {
|
|
43
|
+
const parse = (value) => value
|
|
44
|
+
.replace(/^v/, '')
|
|
45
|
+
.split('-')[0]
|
|
46
|
+
.split('.')
|
|
47
|
+
.map((part) => {
|
|
48
|
+
const parsed = Number.parseInt(part, 10);
|
|
49
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
50
|
+
});
|
|
51
|
+
const left = parse(installed);
|
|
52
|
+
const right = parse(required);
|
|
53
|
+
for (let index = 0; index < 3; index += 1) {
|
|
54
|
+
const diff = (left[index] ?? 0) - (right[index] ?? 0);
|
|
55
|
+
if (diff !== 0)
|
|
56
|
+
return diff < 0;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
function sdkVersionLowerBound(spec) {
|
|
61
|
+
return (0, sdk_range_1.concreteOrStaleSdkSpec)(spec, exports.MANAGED_FUNCTION_CAPABILITIES.features.invoke_timeout.min_sdk);
|
|
62
|
+
}
|
|
63
|
+
function sdkVersionFromPackageJson(raw) {
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(raw);
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
if (!parsed || typeof parsed !== 'object')
|
|
72
|
+
return null;
|
|
73
|
+
const dependencies = Reflect.get(parsed, 'dependencies');
|
|
74
|
+
const devDependencies = Reflect.get(parsed, 'devDependencies');
|
|
75
|
+
return sdkVersionLowerBound(dependencySpec(dependencies) ?? dependencySpec(devDependencies));
|
|
76
|
+
}
|
|
77
|
+
function dependencySpec(block) {
|
|
78
|
+
if (!block || typeof block !== 'object')
|
|
79
|
+
return undefined;
|
|
80
|
+
const value = Reflect.get(block, exports.MANAGED_FUNCTION_SDK_PACKAGE);
|
|
81
|
+
return typeof value === 'string' ? value : undefined;
|
|
82
|
+
}
|
|
83
|
+
function isPostgresEgress(entry) {
|
|
84
|
+
return /^postgres(ql)?:/i.test(entry.trim());
|
|
85
|
+
}
|
|
86
|
+
function formatValidationDetail(issues) {
|
|
87
|
+
const shown = issues.slice(0, 8).map((issue) => {
|
|
88
|
+
const path = (issue.path ?? [])
|
|
89
|
+
.map((part) => String(part))
|
|
90
|
+
.filter((part) => part.length > 0)
|
|
91
|
+
.join('.');
|
|
92
|
+
const message = issue.message?.trim() || 'invalid';
|
|
93
|
+
return path ? `${path}: ${message}` : message;
|
|
94
|
+
});
|
|
95
|
+
if (shown.length === 0)
|
|
96
|
+
return 'Validation error';
|
|
97
|
+
const extra = issues.length > shown.length ? ` (+${issues.length - shown.length} more)` : '';
|
|
98
|
+
return `Validation error: ${shown.join('; ')}${extra}`;
|
|
99
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ export { shouldReportAtlasHttp } from './http-tracing';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
export { MANAGED_FUNCTION_LIMITS, MF_LIMITS_CAPS, MF_LIMITS_DEFAULTS, managedFunctionWorkloadLimitsSchema } from './limits';
|
|
4
4
|
export type { ManagedFunctionLimitCatalog } from './limits';
|
|
5
|
+
export { MANAGED_FUNCTION_CAPABILITIES, MANAGED_FUNCTION_CLI_PACKAGE, MANAGED_FUNCTION_SDK_PACKAGE, formatValidationDetail, isPostgresEgress, sdkVersionFromPackageJson, sdkVersionLowerBound, versionIsOlder, } from './capabilities';
|
|
6
|
+
export type { DeployedPolicy, ManagedFunctionCapabilities, PreflightIssue, PreflightManifest, } from './capabilities';
|
|
7
|
+
export { parseDeployedPolicy } from './deployed-policy';
|
|
8
|
+
export { preflightManagedFunction } from './preflight';
|
|
5
9
|
export declare const CACHE_MAX_TTL_SECONDS: 86400;
|
|
6
10
|
export declare const cacheManifestSchema: z.ZodObject<{
|
|
7
11
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
package/dist/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.cacheManifestSchema = exports.CACHE_MAX_TTL_SECONDS = exports.managedFunctionWorkloadLimitsSchema = exports.MF_LIMITS_DEFAULTS = exports.MF_LIMITS_CAPS = exports.MANAGED_FUNCTION_LIMITS = exports.shouldReportAtlasHttp = void 0;
|
|
3
|
+
exports.cacheManifestSchema = exports.CACHE_MAX_TTL_SECONDS = exports.preflightManagedFunction = exports.parseDeployedPolicy = exports.versionIsOlder = exports.sdkVersionLowerBound = exports.sdkVersionFromPackageJson = exports.isPostgresEgress = exports.formatValidationDetail = exports.MANAGED_FUNCTION_SDK_PACKAGE = exports.MANAGED_FUNCTION_CLI_PACKAGE = exports.MANAGED_FUNCTION_CAPABILITIES = exports.managedFunctionWorkloadLimitsSchema = exports.MF_LIMITS_DEFAULTS = exports.MF_LIMITS_CAPS = exports.MANAGED_FUNCTION_LIMITS = exports.shouldReportAtlasHttp = void 0;
|
|
4
4
|
exports.readCacheConfiguration = readCacheConfiguration;
|
|
5
5
|
var http_tracing_1 = require("./http-tracing");
|
|
6
6
|
Object.defineProperty(exports, "shouldReportAtlasHttp", { enumerable: true, get: function () { return http_tracing_1.shouldReportAtlasHttp; } });
|
|
@@ -11,6 +11,19 @@ Object.defineProperty(exports, "MANAGED_FUNCTION_LIMITS", { enumerable: true, ge
|
|
|
11
11
|
Object.defineProperty(exports, "MF_LIMITS_CAPS", { enumerable: true, get: function () { return limits_2.MF_LIMITS_CAPS; } });
|
|
12
12
|
Object.defineProperty(exports, "MF_LIMITS_DEFAULTS", { enumerable: true, get: function () { return limits_2.MF_LIMITS_DEFAULTS; } });
|
|
13
13
|
Object.defineProperty(exports, "managedFunctionWorkloadLimitsSchema", { enumerable: true, get: function () { return limits_2.managedFunctionWorkloadLimitsSchema; } });
|
|
14
|
+
var capabilities_1 = require("./capabilities");
|
|
15
|
+
Object.defineProperty(exports, "MANAGED_FUNCTION_CAPABILITIES", { enumerable: true, get: function () { return capabilities_1.MANAGED_FUNCTION_CAPABILITIES; } });
|
|
16
|
+
Object.defineProperty(exports, "MANAGED_FUNCTION_CLI_PACKAGE", { enumerable: true, get: function () { return capabilities_1.MANAGED_FUNCTION_CLI_PACKAGE; } });
|
|
17
|
+
Object.defineProperty(exports, "MANAGED_FUNCTION_SDK_PACKAGE", { enumerable: true, get: function () { return capabilities_1.MANAGED_FUNCTION_SDK_PACKAGE; } });
|
|
18
|
+
Object.defineProperty(exports, "formatValidationDetail", { enumerable: true, get: function () { return capabilities_1.formatValidationDetail; } });
|
|
19
|
+
Object.defineProperty(exports, "isPostgresEgress", { enumerable: true, get: function () { return capabilities_1.isPostgresEgress; } });
|
|
20
|
+
Object.defineProperty(exports, "sdkVersionFromPackageJson", { enumerable: true, get: function () { return capabilities_1.sdkVersionFromPackageJson; } });
|
|
21
|
+
Object.defineProperty(exports, "sdkVersionLowerBound", { enumerable: true, get: function () { return capabilities_1.sdkVersionLowerBound; } });
|
|
22
|
+
Object.defineProperty(exports, "versionIsOlder", { enumerable: true, get: function () { return capabilities_1.versionIsOlder; } });
|
|
23
|
+
var deployed_policy_1 = require("./deployed-policy");
|
|
24
|
+
Object.defineProperty(exports, "parseDeployedPolicy", { enumerable: true, get: function () { return deployed_policy_1.parseDeployedPolicy; } });
|
|
25
|
+
var preflight_1 = require("./preflight");
|
|
26
|
+
Object.defineProperty(exports, "preflightManagedFunction", { enumerable: true, get: function () { return preflight_1.preflightManagedFunction; } });
|
|
14
27
|
const limits = limits_1.MANAGED_FUNCTION_LIMITS.cache;
|
|
15
28
|
exports.CACHE_MAX_TTL_SECONDS = limits.default_ttl_seconds.max;
|
|
16
29
|
exports.cacheManifestSchema = zod_1.z
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseDeployedPolicy = parseDeployedPolicy;
|
|
4
|
+
function isLimitCatalog(value) {
|
|
5
|
+
if (!value || typeof value !== 'object')
|
|
6
|
+
return false;
|
|
7
|
+
const workload = Reflect.get(value, 'workload');
|
|
8
|
+
return !!workload && typeof workload === 'object';
|
|
9
|
+
}
|
|
10
|
+
function isCapabilities(value) {
|
|
11
|
+
if (!value || typeof value !== 'object')
|
|
12
|
+
return false;
|
|
13
|
+
const tooling = Reflect.get(value, 'tooling');
|
|
14
|
+
const features = Reflect.get(value, 'features');
|
|
15
|
+
if (!tooling || typeof tooling !== 'object' || !features || typeof features !== 'object')
|
|
16
|
+
return false;
|
|
17
|
+
const cli = Reflect.get(tooling, 'cli');
|
|
18
|
+
const sdk = Reflect.get(tooling, 'sdk');
|
|
19
|
+
return hasPackageMin(cli) && hasPackageMin(sdk);
|
|
20
|
+
}
|
|
21
|
+
function hasPackageMin(value) {
|
|
22
|
+
if (!value || typeof value !== 'object')
|
|
23
|
+
return false;
|
|
24
|
+
return typeof Reflect.get(value, 'package') === 'string' && typeof Reflect.get(value, 'min') === 'string';
|
|
25
|
+
}
|
|
26
|
+
function parseDeployedPolicy(body) {
|
|
27
|
+
if (!body || typeof body !== 'object')
|
|
28
|
+
return { limits: null, capabilities: null };
|
|
29
|
+
const limits = Reflect.get(body, 'limits');
|
|
30
|
+
const capabilities = Reflect.get(body, 'capabilities');
|
|
31
|
+
return {
|
|
32
|
+
limits: isLimitCatalog(limits) ? limits : null,
|
|
33
|
+
capabilities: isCapabilities(capabilities) ? normalizeCapabilities(capabilities) : null,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function normalizeCapabilities(value) {
|
|
37
|
+
const features = value.features;
|
|
38
|
+
return {
|
|
39
|
+
tooling: value.tooling,
|
|
40
|
+
features: {
|
|
41
|
+
postgres_egress: normalizePostgres(features.postgres_egress),
|
|
42
|
+
http_headers: normalizeHeaders(features.http_headers),
|
|
43
|
+
invoke_timeout: normalizeTimeout(features.invoke_timeout),
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function normalizePostgres(value) {
|
|
48
|
+
if (!value || typeof value.supported !== 'boolean' || typeof value.limit !== 'string')
|
|
49
|
+
return undefined;
|
|
50
|
+
if (typeof value.min_cli !== 'string' || typeof value.min_sdk !== 'string')
|
|
51
|
+
return undefined;
|
|
52
|
+
return {
|
|
53
|
+
field: typeof value.field === 'string' ? value.field : 'egress',
|
|
54
|
+
supported: value.supported,
|
|
55
|
+
limit: value.limit,
|
|
56
|
+
min_cli: value.min_cli,
|
|
57
|
+
min_sdk: value.min_sdk,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function normalizeHeaders(value) {
|
|
61
|
+
if (!value || typeof value.max_bytes !== 'number' || typeof value.limit !== 'string')
|
|
62
|
+
return undefined;
|
|
63
|
+
if (typeof value.min_cli !== 'string' || typeof value.min_sdk !== 'string')
|
|
64
|
+
return undefined;
|
|
65
|
+
return {
|
|
66
|
+
field: typeof value.field === 'string' ? value.field : 'invocation.headers',
|
|
67
|
+
max_bytes: value.max_bytes,
|
|
68
|
+
limit: value.limit,
|
|
69
|
+
min_cli: value.min_cli,
|
|
70
|
+
min_sdk: value.min_sdk,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function normalizeTimeout(value) {
|
|
74
|
+
if (!value || typeof value.limit !== 'string')
|
|
75
|
+
return undefined;
|
|
76
|
+
if (typeof value.max_seconds !== 'number' || typeof value.legacy_sdk_max_seconds !== 'number')
|
|
77
|
+
return undefined;
|
|
78
|
+
if (typeof value.min_cli !== 'string' || typeof value.min_sdk !== 'string')
|
|
79
|
+
return undefined;
|
|
80
|
+
return {
|
|
81
|
+
field: typeof value.field === 'string' ? value.field : 'limits.timeout_seconds',
|
|
82
|
+
max_seconds: value.max_seconds,
|
|
83
|
+
legacy_sdk_max_seconds: value.legacy_sdk_max_seconds,
|
|
84
|
+
limit: value.limit,
|
|
85
|
+
min_cli: value.min_cli,
|
|
86
|
+
min_sdk: value.min_sdk,
|
|
87
|
+
};
|
|
88
|
+
}
|
package/dist/limits.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare const MANAGED_FUNCTION_LIMITS: {
|
|
|
15
15
|
};
|
|
16
16
|
readonly timeout_seconds: {
|
|
17
17
|
readonly min: 1;
|
|
18
|
-
readonly max:
|
|
18
|
+
readonly max: 3600;
|
|
19
19
|
readonly default: 60;
|
|
20
20
|
readonly unit: "seconds";
|
|
21
21
|
};
|
|
@@ -44,9 +44,11 @@ export declare const MANAGED_FUNCTION_LIMITS: {
|
|
|
44
44
|
readonly configurable: false;
|
|
45
45
|
};
|
|
46
46
|
readonly max_response_bytes: {
|
|
47
|
+
readonly min: 1;
|
|
47
48
|
readonly max: number;
|
|
49
|
+
readonly default: number;
|
|
48
50
|
readonly unit: "bytes";
|
|
49
|
-
readonly configurable:
|
|
51
|
+
readonly configurable: true;
|
|
50
52
|
};
|
|
51
53
|
readonly cpu: {
|
|
52
54
|
readonly configurable: true;
|
|
@@ -126,6 +128,7 @@ export declare const managedFunctionWorkloadLimitsSchema: z.ZodPrefault<z.ZodObj
|
|
|
126
128
|
cpu: z.ZodDefault<z.ZodUnion<readonly [z.ZodLiteral<"auto">, z.ZodNumber]>>;
|
|
127
129
|
concurrency: z.ZodDefault<z.ZodNumber>;
|
|
128
130
|
timeout_seconds: z.ZodDefault<z.ZodNumber>;
|
|
131
|
+
max_response_bytes: z.ZodDefault<z.ZodNumber>;
|
|
129
132
|
min_instances: z.ZodDefault<z.ZodNumber>;
|
|
130
133
|
max_instances: z.ZodDefault<z.ZodNumber>;
|
|
131
134
|
invoke_rate_per_minute: z.ZodDefault<z.ZodNumber>;
|
|
@@ -134,7 +137,7 @@ export declare const MF_LIMITS_CAPS: {
|
|
|
134
137
|
readonly cpu: 2;
|
|
135
138
|
readonly concurrency: 80;
|
|
136
139
|
readonly memoryMb: 2048;
|
|
137
|
-
readonly timeoutSeconds:
|
|
140
|
+
readonly timeoutSeconds: 3600;
|
|
138
141
|
readonly maxInstances: 10;
|
|
139
142
|
readonly invokeRatePerMinute: 600;
|
|
140
143
|
readonly maxRequestBytes: number;
|
package/dist/limits.js
CHANGED
|
@@ -8,12 +8,18 @@ exports.MANAGED_FUNCTION_LIMITS = {
|
|
|
8
8
|
},
|
|
9
9
|
workload: {
|
|
10
10
|
memory_mb: { min: 128, max: 2048, default: 256, unit: 'MiB per instance' },
|
|
11
|
-
timeout_seconds: { min: 1, max:
|
|
11
|
+
timeout_seconds: { min: 1, max: 3600, default: 60, unit: 'seconds' },
|
|
12
12
|
min_instances: { min: 0, max: 10, default: 1, scope: 'Cannot exceed max_instances; warm instances incur idle charges' },
|
|
13
13
|
max_instances: { min: 2, max: 10, default: 3, scope: 'Autoscaling ceiling; must allow at least two instances, and GCP may temporarily exceed it' },
|
|
14
14
|
invoke_rate_per_minute: { min: 1, max: 600, default: 60, window_seconds: 60, scope: 'Per function across all Atlas replicas in the environment; Redis-backed fixed window. Redis unavailability rejects invocations (503); key loss resets the window' },
|
|
15
15
|
max_request_bytes: { max: 1024 * 1024, unit: 'bytes', configurable: false },
|
|
16
|
-
max_response_bytes: {
|
|
16
|
+
max_response_bytes: {
|
|
17
|
+
min: 1,
|
|
18
|
+
max: 256 * 1024 * 1024,
|
|
19
|
+
default: 4 * 1024 * 1024,
|
|
20
|
+
unit: 'bytes',
|
|
21
|
+
configurable: true,
|
|
22
|
+
},
|
|
17
23
|
cpu: {
|
|
18
24
|
configurable: true, default: 'auto', min: 1, max: 2,
|
|
19
25
|
min_memory_mb_for_multiple_cpus: 512,
|
|
@@ -51,6 +57,7 @@ exports.managedFunctionWorkloadLimitsSchema = zod_1.z.object({
|
|
|
51
57
|
]).default(workload.cpu.default),
|
|
52
58
|
concurrency: integerLimit(workload.concurrency),
|
|
53
59
|
timeout_seconds: integerLimit(workload.timeout_seconds),
|
|
60
|
+
max_response_bytes: integerLimit(workload.max_response_bytes),
|
|
54
61
|
min_instances: integerLimit(workload.min_instances),
|
|
55
62
|
max_instances: integerLimit(workload.max_instances),
|
|
56
63
|
invoke_rate_per_minute: integerLimit(workload.invoke_rate_per_minute),
|
|
@@ -94,5 +101,5 @@ exports.MF_LIMITS_DEFAULTS = {
|
|
|
94
101
|
maxInstances: workload.max_instances.default,
|
|
95
102
|
invokeRatePerMinute: workload.invoke_rate_per_minute.default,
|
|
96
103
|
maxRequestBytes: workload.max_request_bytes.max,
|
|
97
|
-
maxResponseBytes: workload.max_response_bytes.
|
|
104
|
+
maxResponseBytes: workload.max_response_bytes.default,
|
|
98
105
|
};
|
package/dist/orm.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
declare class OrmClientError extends Error {
|
|
2
|
+
readonly status: number | null;
|
|
3
|
+
readonly name: string;
|
|
4
|
+
constructor(message: string, status?: number | null);
|
|
5
|
+
}
|
|
6
|
+
interface OperationErrorEntry {
|
|
7
|
+
readonly message: string;
|
|
8
|
+
readonly extensions?: {
|
|
9
|
+
readonly code?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
declare class OrmOperationError extends OrmClientError {
|
|
13
|
+
readonly code: string | null;
|
|
14
|
+
readonly errors: readonly OperationErrorEntry[];
|
|
15
|
+
readonly name = "OrmOperationError";
|
|
16
|
+
constructor(message: string, status: number | null, code: string | null, errors?: readonly OperationErrorEntry[]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** One entry of `operations.manifest.json` (generated by `orm generate`). */
|
|
20
|
+
interface ManifestOperation {
|
|
21
|
+
readonly hash: string;
|
|
22
|
+
readonly kind: 'query' | 'mutation';
|
|
23
|
+
readonly requires: {
|
|
24
|
+
readonly capability?: string;
|
|
25
|
+
readonly role?: string;
|
|
26
|
+
} | null;
|
|
27
|
+
}
|
|
28
|
+
interface OperationsManifest {
|
|
29
|
+
readonly schemaHash: string;
|
|
30
|
+
readonly opsHash?: string;
|
|
31
|
+
readonly operations: Readonly<Record<string, ManifestOperation>>;
|
|
32
|
+
}
|
|
33
|
+
interface OperationExecutionOptions {
|
|
34
|
+
/** Reuse only when retrying the same logical mutation. */
|
|
35
|
+
idempotencyKey?: string;
|
|
36
|
+
}
|
|
37
|
+
interface OperationExecutor {
|
|
38
|
+
/** Execute a manifest operation by name (or a raw `sha256:` hash). */
|
|
39
|
+
execute<TData = Record<string, unknown>>(op: string, variables?: Record<string, unknown>, options?: OperationExecutionOptions): Promise<TData>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type FunctionOrmErrorCode = 'INVALID_CONFIGURATION' | 'MISSING_CALLER_CONTEXT' | 'INVALID_CALLER_CONTEXT' | 'UNSUPPORTED_CALLER' | 'EXPIRED_CALLER_CONTEXT';
|
|
43
|
+
declare class FunctionOrmError extends Error {
|
|
44
|
+
readonly code: FunctionOrmErrorCode;
|
|
45
|
+
readonly name = "FunctionOrmError";
|
|
46
|
+
constructor(code: FunctionOrmErrorCode, message: string);
|
|
47
|
+
}
|
|
48
|
+
interface FunctionOrmRequest {
|
|
49
|
+
readonly headers: Pick<Headers, 'get'> | Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
50
|
+
}
|
|
51
|
+
declare const ORM_JSON_SCALARS_HEADER = "x-sequence-orm-json-scalars";
|
|
52
|
+
declare const ORM_LOSSLESS_JSON_SCALARS = "lossless-v1";
|
|
53
|
+
type FunctionOrmJsonScalars = typeof ORM_LOSSLESS_JSON_SCALARS;
|
|
54
|
+
interface FunctionOrmExecutorOptions {
|
|
55
|
+
request: FunctionOrmRequest;
|
|
56
|
+
baseUrl: string;
|
|
57
|
+
namespace: string;
|
|
58
|
+
manifest: unknown;
|
|
59
|
+
fetchImpl?: typeof globalThis.fetch;
|
|
60
|
+
jsonScalars?: FunctionOrmJsonScalars;
|
|
61
|
+
}
|
|
62
|
+
declare function createFunctionOrmExecutor({ request, fetchImpl, ...options }: FunctionOrmExecutorOptions): OperationExecutor;
|
|
63
|
+
declare function createFunctionServiceOrmExecutor({ request, fetchImpl, ...options }: FunctionOrmExecutorOptions): OperationExecutor;
|
|
64
|
+
|
|
65
|
+
export { FunctionOrmError, type FunctionOrmErrorCode, type FunctionOrmExecutorOptions, type FunctionOrmJsonScalars, type FunctionOrmRequest, ORM_JSON_SCALARS_HEADER, ORM_LOSSLESS_JSON_SCALARS, type OperationExecutor, type OperationsManifest, OrmClientError, OrmOperationError, createFunctionOrmExecutor, createFunctionServiceOrmExecutor };
|