@sequenceholdings/managed-functions 0.2.0 → 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 +18 -3
- 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 +12 -3
- package/dist/orm.js +104 -31
- 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 +2 -2
package/dist/orm.js
CHANGED
|
@@ -21,15 +21,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var orm_exports = {};
|
|
22
22
|
__export(orm_exports, {
|
|
23
23
|
FunctionOrmError: () => FunctionOrmError,
|
|
24
|
+
ORM_JSON_SCALARS_HEADER: () => ORM_JSON_SCALARS_HEADER,
|
|
25
|
+
ORM_LOSSLESS_JSON_SCALARS: () => ORM_LOSSLESS_JSON_SCALARS,
|
|
24
26
|
OrmClientError: () => OrmClientError,
|
|
25
27
|
OrmOperationError: () => OrmOperationError,
|
|
26
|
-
createFunctionOrmExecutor: () => createFunctionOrmExecutor
|
|
28
|
+
createFunctionOrmExecutor: () => createFunctionOrmExecutor,
|
|
29
|
+
createFunctionServiceOrmExecutor: () => createFunctionServiceOrmExecutor
|
|
27
30
|
});
|
|
28
31
|
module.exports = __toCommonJS(orm_exports);
|
|
29
32
|
|
|
30
33
|
// ../orm/dist/client.js
|
|
31
34
|
var NAMESPACE_PATTERN = /^[a-z][a-z0-9_]{0,40}$/;
|
|
32
35
|
var ORM_DATA_TOKEN_HEADER = "x-sequence-data-token";
|
|
36
|
+
var ORM_SERVICE_DATA_TOKEN_HEADER = "x-sequence-data-token-service";
|
|
33
37
|
var MATERIALIZE_ACTOR = "orm:materialize";
|
|
34
38
|
JSON.stringify({ sub: MATERIALIZE_ACTOR, org: null, roles: [], capabilities: [] });
|
|
35
39
|
var OrmClientError = class extends Error {
|
|
@@ -78,6 +82,11 @@ function readOperationResponse({
|
|
|
78
82
|
}
|
|
79
83
|
return payload.data;
|
|
80
84
|
}
|
|
85
|
+
function validateIdempotencyKey(key) {
|
|
86
|
+
if (key !== void 0 && !/^[A-Za-z0-9._:-]{1,128}$/.test(key)) {
|
|
87
|
+
throw new OrmClientError("idempotencyKey must be 1\u2013128 letters, digits, dots, underscores, colons or hyphens");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
81
90
|
var operationHash = /^sha256:[a-f0-9]{64}$/;
|
|
82
91
|
function readManifestHashes(manifest) {
|
|
83
92
|
if (!isRecord(manifest) || typeof manifest.schemaHash !== "string" || !manifest.schemaHash || !isRecord(manifest.operations)) {
|
|
@@ -96,18 +105,25 @@ function createOperationExecutorWithTransport({ namespace, manifest, transport }
|
|
|
96
105
|
if (typeof namespace !== "string" || !NAMESPACE_PATTERN.test(namespace)) throw new OrmClientError("invalid ORM namespace \u2014 expected a lowercase identifier of at most 41 characters");
|
|
97
106
|
const hashes = readManifestHashes(manifest);
|
|
98
107
|
const knownHashes = new Set(hashes.values());
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
108
|
+
async function execute(op, variables = {}, options = {}) {
|
|
109
|
+
validateIdempotencyKey(options.idempotencyKey);
|
|
110
|
+
if (typeof op !== "string" && op.namespace !== namespace) throw new OrmClientError("operation belongs to a different namespace");
|
|
111
|
+
const selector = typeof op === "string" ? op : op.hash;
|
|
112
|
+
const hash = hashes.get(selector) ?? (knownHashes.has(selector) ? selector : void 0);
|
|
113
|
+
if (!hash) throw new OrmClientError(`unknown operation "${op}" \u2014 not in the manifest (${hashes.size} known). Re-run seq-studio orm generate.`);
|
|
114
|
+
if (!isRecord(variables)) throw new OrmClientError("operation variables must be an object");
|
|
115
|
+
const response = await transport({
|
|
116
|
+
namespace,
|
|
117
|
+
op: hash,
|
|
118
|
+
variables,
|
|
119
|
+
...options.idempotencyKey === void 0 ? {} : { idempotencyKey: options.idempotencyKey }
|
|
120
|
+
});
|
|
121
|
+
if (!response || typeof response.ok !== "boolean" || !Number.isInteger(response.status) || response.status < 100 || response.status > 599 || response.ok !== (response.status >= 200 && response.status < 300)) {
|
|
122
|
+
throw new OrmOperationError("malformed transport response", null, null);
|
|
109
123
|
}
|
|
110
|
-
|
|
124
|
+
return readOperationResponse(response);
|
|
125
|
+
}
|
|
126
|
+
return { execute };
|
|
111
127
|
}
|
|
112
128
|
|
|
113
129
|
// src/orm.ts
|
|
@@ -118,21 +134,20 @@ var FunctionOrmError = class extends Error {
|
|
|
118
134
|
}
|
|
119
135
|
name = "FunctionOrmError";
|
|
120
136
|
};
|
|
121
|
-
|
|
137
|
+
var ORM_JSON_SCALARS_HEADER = "x-sequence-orm-json-scalars";
|
|
138
|
+
var ORM_LOSSLESS_JSON_SCALARS = "lossless-v1";
|
|
139
|
+
function readToken({ request, header, missing }) {
|
|
122
140
|
const { headers } = request;
|
|
123
141
|
let value;
|
|
124
142
|
if (typeof headers.get === "function") {
|
|
125
|
-
value = headers.get(
|
|
143
|
+
value = headers.get(header);
|
|
126
144
|
} else {
|
|
127
|
-
const matches = Object.entries(headers).filter(([name]) => name.toLowerCase() ===
|
|
145
|
+
const matches = Object.entries(headers).filter(([name]) => name.toLowerCase() === header);
|
|
128
146
|
if (matches.length > 1) throw invalidContext();
|
|
129
147
|
value = matches[0]?.[1];
|
|
130
148
|
}
|
|
131
149
|
if (value === void 0 || value === null || value === "") {
|
|
132
|
-
throw new FunctionOrmError(
|
|
133
|
-
"MISSING_CALLER_CONTEXT",
|
|
134
|
-
"Caller ORM access is unavailable. Declare the function data binding and invoke it through the platform as a signed-in user."
|
|
135
|
-
);
|
|
150
|
+
throw new FunctionOrmError("MISSING_CALLER_CONTEXT", missing);
|
|
136
151
|
}
|
|
137
152
|
if (typeof value !== "string" || value.length > 32768) throw invalidContext();
|
|
138
153
|
return value;
|
|
@@ -143,7 +158,7 @@ function invalidContext() {
|
|
|
143
158
|
function record(value) {
|
|
144
159
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
145
160
|
}
|
|
146
|
-
function
|
|
161
|
+
function invocationClaims({ token }) {
|
|
147
162
|
const parts = /^([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]{43})$/.exec(token);
|
|
148
163
|
if (!parts?.[1]) throw invalidContext();
|
|
149
164
|
let claims;
|
|
@@ -153,10 +168,24 @@ function callerExpiry({ token }) {
|
|
|
153
168
|
throw invalidContext();
|
|
154
169
|
}
|
|
155
170
|
if (!record(claims) || typeof claims.sub !== "string" || !claims.sub || typeof claims.functionId !== "string" || !claims.functionId || typeof claims.invocationId !== "string" || !claims.invocationId || typeof claims.exp !== "number" || !Number.isSafeInteger(claims.exp) || claims.origin !== void 0 && claims.origin !== "function") throw invalidContext();
|
|
171
|
+
return { sub: claims.sub, exp: claims.exp, assertedBy: claims.assertedBy };
|
|
172
|
+
}
|
|
173
|
+
function callerExpiry({ token }) {
|
|
174
|
+
const claims = invocationClaims({ token });
|
|
156
175
|
if (claims.sub.startsWith("service-account|") || claims.sub.endsWith("@clients") || claims.assertedBy != null) {
|
|
157
176
|
throw new FunctionOrmError(
|
|
158
177
|
"UNSUPPORTED_CALLER",
|
|
159
|
-
"This ORM helper requires a direct human invocation.
|
|
178
|
+
"This ORM helper requires a direct human invocation. For system-runner (Lattice) invocations, use createFunctionServiceOrmExecutor to act as the function's attached service account."
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return claims.exp;
|
|
182
|
+
}
|
|
183
|
+
function serviceExpiry({ token }) {
|
|
184
|
+
const claims = invocationClaims({ token });
|
|
185
|
+
if (!claims.sub.startsWith("service-account|") || typeof claims.assertedBy !== "string" || !claims.assertedBy) {
|
|
186
|
+
throw new FunctionOrmError(
|
|
187
|
+
"UNSUPPORTED_CALLER",
|
|
188
|
+
"The service data token does not name the function's attached service account."
|
|
160
189
|
);
|
|
161
190
|
}
|
|
162
191
|
return claims.exp;
|
|
@@ -185,40 +214,84 @@ function trustedOrigin({ baseUrl }) {
|
|
|
185
214
|
}
|
|
186
215
|
return url.origin;
|
|
187
216
|
}
|
|
188
|
-
function
|
|
189
|
-
request,
|
|
217
|
+
function createExecutor({
|
|
190
218
|
baseUrl,
|
|
191
219
|
namespace,
|
|
192
220
|
manifest,
|
|
193
|
-
fetchImpl
|
|
221
|
+
fetchImpl,
|
|
222
|
+
token,
|
|
223
|
+
header,
|
|
224
|
+
expiresAt,
|
|
225
|
+
jsonScalars
|
|
194
226
|
}) {
|
|
195
227
|
const origin = trustedOrigin({ baseUrl });
|
|
196
|
-
const token = readToken({ request });
|
|
197
|
-
const expiresAt = callerExpiry({ token });
|
|
198
228
|
requireUnexpired({ expiresAt });
|
|
199
229
|
if (typeof fetchImpl !== "function") {
|
|
200
|
-
throw new FunctionOrmError("INVALID_CONFIGURATION", "
|
|
230
|
+
throw new FunctionOrmError("INVALID_CONFIGURATION", "Function ORM access requires a fetch implementation.");
|
|
231
|
+
}
|
|
232
|
+
if (jsonScalars !== void 0 && jsonScalars !== ORM_LOSSLESS_JSON_SCALARS) {
|
|
233
|
+
throw new FunctionOrmError("INVALID_CONFIGURATION", `jsonScalars must be ${ORM_LOSSLESS_JSON_SCALARS} when supplied.`);
|
|
201
234
|
}
|
|
202
235
|
return createOperationExecutorWithTransport({
|
|
203
236
|
namespace,
|
|
204
237
|
manifest,
|
|
205
|
-
transport: async ({ namespace: targetNamespace, op, variables }) => {
|
|
238
|
+
transport: async ({ namespace: targetNamespace, op, variables, idempotencyKey }) => {
|
|
206
239
|
requireUnexpired({ expiresAt });
|
|
207
240
|
const response = await fetchImpl(`${origin}/_data/v2/${targetNamespace}`, {
|
|
208
241
|
method: "POST",
|
|
209
|
-
headers: {
|
|
210
|
-
|
|
242
|
+
headers: {
|
|
243
|
+
"Content-Type": "application/json",
|
|
244
|
+
[header]: token,
|
|
245
|
+
...jsonScalars ? { [ORM_JSON_SCALARS_HEADER]: jsonScalars } : {}
|
|
246
|
+
},
|
|
247
|
+
body: JSON.stringify({ op, variables, idempotencyKey }),
|
|
211
248
|
redirect: "error"
|
|
212
249
|
});
|
|
213
250
|
const body = await response.json().catch(() => null);
|
|
251
|
+
if (jsonScalars && response.ok && response.headers.get(ORM_JSON_SCALARS_HEADER) !== jsonScalars) {
|
|
252
|
+
throw new OrmOperationError(
|
|
253
|
+
`Atlas did not confirm ${jsonScalars} numbers for this ORM response, so its numeric and bigint values may be rounded; update Atlas before relying on exact numbers`,
|
|
254
|
+
response.status,
|
|
255
|
+
"LOSSLESS_TRANSPORT_UNAVAILABLE"
|
|
256
|
+
);
|
|
257
|
+
}
|
|
214
258
|
return { ok: response.ok, status: response.status, body };
|
|
215
259
|
}
|
|
216
260
|
});
|
|
217
261
|
}
|
|
262
|
+
function createFunctionOrmExecutor({
|
|
263
|
+
request,
|
|
264
|
+
fetchImpl = globalThis.fetch,
|
|
265
|
+
...options
|
|
266
|
+
}) {
|
|
267
|
+
trustedOrigin({ baseUrl: options.baseUrl });
|
|
268
|
+
const token = readToken({
|
|
269
|
+
request,
|
|
270
|
+
header: ORM_DATA_TOKEN_HEADER,
|
|
271
|
+
missing: "Caller ORM access is unavailable. Declare the function data binding and invoke it through the platform as a signed-in user."
|
|
272
|
+
});
|
|
273
|
+
return createExecutor({ ...options, fetchImpl, token, header: ORM_DATA_TOKEN_HEADER, expiresAt: callerExpiry({ token }) });
|
|
274
|
+
}
|
|
275
|
+
function createFunctionServiceOrmExecutor({
|
|
276
|
+
request,
|
|
277
|
+
fetchImpl = globalThis.fetch,
|
|
278
|
+
...options
|
|
279
|
+
}) {
|
|
280
|
+
trustedOrigin({ baseUrl: options.baseUrl });
|
|
281
|
+
const token = readToken({
|
|
282
|
+
request,
|
|
283
|
+
header: ORM_SERVICE_DATA_TOKEN_HEADER,
|
|
284
|
+
missing: "Service ORM access is unavailable. Declare service_account and the data binding in managed-function.yml and invoke the function through the platform."
|
|
285
|
+
});
|
|
286
|
+
return createExecutor({ ...options, fetchImpl, token, header: ORM_SERVICE_DATA_TOKEN_HEADER, expiresAt: serviceExpiry({ token }) });
|
|
287
|
+
}
|
|
218
288
|
// Annotate the CommonJS export names for ESM import in node:
|
|
219
289
|
0 && (module.exports = {
|
|
220
290
|
FunctionOrmError,
|
|
291
|
+
ORM_JSON_SCALARS_HEADER,
|
|
292
|
+
ORM_LOSSLESS_JSON_SCALARS,
|
|
221
293
|
OrmClientError,
|
|
222
294
|
OrmOperationError,
|
|
223
|
-
createFunctionOrmExecutor
|
|
295
|
+
createFunctionOrmExecutor,
|
|
296
|
+
createFunctionServiceOrmExecutor
|
|
224
297
|
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ManagedFunctionLimitCatalog } from './limits';
|
|
2
|
+
import { type ManagedFunctionCapabilities, type PreflightIssue, type PreflightManifest } from './capabilities';
|
|
3
|
+
export declare function preflightManagedFunction({ manifest, limits, capabilities, cliVersion, sdkVersion, }: {
|
|
4
|
+
manifest: PreflightManifest;
|
|
5
|
+
limits: ManagedFunctionLimitCatalog | null;
|
|
6
|
+
capabilities: ManagedFunctionCapabilities | null;
|
|
7
|
+
cliVersion: string;
|
|
8
|
+
sdkVersion: string | null;
|
|
9
|
+
}): PreflightIssue[];
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.preflightManagedFunction = preflightManagedFunction;
|
|
4
|
+
const capabilities_1 = require("./capabilities");
|
|
5
|
+
const sdk_range_1 = require("./sdk-range");
|
|
6
|
+
const WORKLOAD_FIELDS = [
|
|
7
|
+
'memory_mb',
|
|
8
|
+
'timeout_seconds',
|
|
9
|
+
'min_instances',
|
|
10
|
+
'max_instances',
|
|
11
|
+
'invoke_rate_per_minute',
|
|
12
|
+
'max_response_bytes',
|
|
13
|
+
'concurrency',
|
|
14
|
+
];
|
|
15
|
+
function preflightManagedFunction({ manifest, limits, capabilities, cliVersion, sdkVersion, }) {
|
|
16
|
+
const tooling = capabilities?.tooling ?? capabilities_1.MANAGED_FUNCTION_CAPABILITIES.tooling;
|
|
17
|
+
const issues = [];
|
|
18
|
+
checkWorkload({ manifest, limits, tooling, cliVersion, sdkVersion, issues });
|
|
19
|
+
checkPostgres({ manifest, capabilities, tooling, cliVersion, sdkVersion, issues });
|
|
20
|
+
checkHeaders({ manifest, capabilities, tooling, cliVersion, sdkVersion, issues });
|
|
21
|
+
checkSdkTimeout({ manifest, limits, capabilities, tooling, cliVersion, sdkVersion, issues });
|
|
22
|
+
return issues;
|
|
23
|
+
}
|
|
24
|
+
function issue({ field, problem, limit, tooling, requiredCli, requiredSdk, cliVersion, sdkVersion, }) {
|
|
25
|
+
const installedSdk = sdkVersion ?? 'not declared';
|
|
26
|
+
return {
|
|
27
|
+
field,
|
|
28
|
+
limit,
|
|
29
|
+
requiredCli,
|
|
30
|
+
requiredSdk,
|
|
31
|
+
message: `${field}: ${problem} Supported limit: ${limit}. ` +
|
|
32
|
+
`Requires ${tooling.cli.package} >= ${requiredCli} (installed ${cliVersion}) ` +
|
|
33
|
+
`and ${tooling.sdk.package} >= ${requiredSdk} (installed ${installedSdk}).`,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function numericCap(entry) {
|
|
37
|
+
if (!entry || typeof entry !== 'object')
|
|
38
|
+
return null;
|
|
39
|
+
const min = Reflect.get(entry, 'min');
|
|
40
|
+
const max = Reflect.get(entry, 'max');
|
|
41
|
+
const unit = Reflect.get(entry, 'unit');
|
|
42
|
+
if (typeof min !== 'number' || typeof max !== 'number')
|
|
43
|
+
return null;
|
|
44
|
+
return { min, max, unit: typeof unit === 'string' ? unit : undefined };
|
|
45
|
+
}
|
|
46
|
+
function checkWorkload({ manifest, limits, tooling, cliVersion, sdkVersion, issues, }) {
|
|
47
|
+
const workload = limits?.workload;
|
|
48
|
+
if (!workload)
|
|
49
|
+
return;
|
|
50
|
+
const declared = manifest.limits;
|
|
51
|
+
if (!declared)
|
|
52
|
+
return;
|
|
53
|
+
for (const name of WORKLOAD_FIELDS) {
|
|
54
|
+
const value = declared[name];
|
|
55
|
+
if (typeof value !== 'number')
|
|
56
|
+
continue;
|
|
57
|
+
const cap = numericCap(workload[name]);
|
|
58
|
+
if (!cap)
|
|
59
|
+
continue;
|
|
60
|
+
if (Number.isInteger(value) && value >= cap.min && value <= cap.max)
|
|
61
|
+
continue;
|
|
62
|
+
const unit = cap.unit ? ` ${cap.unit}` : '';
|
|
63
|
+
issues.push(issue({
|
|
64
|
+
field: `limits.${name}`,
|
|
65
|
+
problem: `${value} is outside the range this deployment enforces`,
|
|
66
|
+
limit: `${cap.min} to ${cap.max}${unit}`,
|
|
67
|
+
tooling,
|
|
68
|
+
requiredCli: tooling.cli.min,
|
|
69
|
+
requiredSdk: tooling.sdk.min,
|
|
70
|
+
cliVersion,
|
|
71
|
+
sdkVersion,
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
const cpu = declared.cpu;
|
|
75
|
+
const cpuCap = numericCap(workload.cpu);
|
|
76
|
+
if (cpu === undefined || cpu === 'auto' || !cpuCap)
|
|
77
|
+
return;
|
|
78
|
+
if (typeof cpu === 'number' && Number.isInteger(cpu) && cpu >= cpuCap.min && cpu <= cpuCap.max)
|
|
79
|
+
return;
|
|
80
|
+
issues.push(issue({
|
|
81
|
+
field: 'limits.cpu',
|
|
82
|
+
problem: `${String(cpu)} is outside the range this deployment enforces`,
|
|
83
|
+
limit: `${cpuCap.min} to ${cpuCap.max}, or auto`,
|
|
84
|
+
tooling,
|
|
85
|
+
requiredCli: tooling.cli.min,
|
|
86
|
+
requiredSdk: tooling.sdk.min,
|
|
87
|
+
cliVersion,
|
|
88
|
+
sdkVersion,
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
function checkPostgres({ manifest, capabilities, tooling, cliVersion, sdkVersion, issues, }) {
|
|
92
|
+
const feature = capabilities?.features.postgres_egress;
|
|
93
|
+
if (!feature)
|
|
94
|
+
return;
|
|
95
|
+
const entries = manifest.egress ?? [];
|
|
96
|
+
entries.forEach((entry, index) => {
|
|
97
|
+
if (!(0, capabilities_1.isPostgresEgress)(entry))
|
|
98
|
+
return;
|
|
99
|
+
const field = `egress[${index}]`;
|
|
100
|
+
if (!feature.supported) {
|
|
101
|
+
issues.push(issue({
|
|
102
|
+
field,
|
|
103
|
+
problem: 'PostgreSQL egress is not supported by this deployment',
|
|
104
|
+
limit: 'hostnames, http(s) URLs, and public IPv4 CIDRs',
|
|
105
|
+
tooling,
|
|
106
|
+
requiredCli: feature.min_cli,
|
|
107
|
+
requiredSdk: feature.min_sdk,
|
|
108
|
+
cliVersion,
|
|
109
|
+
sdkVersion,
|
|
110
|
+
}));
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if ((0, capabilities_1.versionIsOlder)(cliVersion, feature.min_cli)) {
|
|
114
|
+
issues.push(issue({
|
|
115
|
+
field,
|
|
116
|
+
problem: 'this CLI rejects PostgreSQL egress that this deployment supports',
|
|
117
|
+
limit: feature.limit,
|
|
118
|
+
tooling,
|
|
119
|
+
requiredCli: feature.min_cli,
|
|
120
|
+
requiredSdk: feature.min_sdk,
|
|
121
|
+
cliVersion,
|
|
122
|
+
sdkVersion,
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function needsWideHeaders(manifest) {
|
|
128
|
+
const data = manifest.capabilities?.data;
|
|
129
|
+
const storage = manifest.capabilities?.storage;
|
|
130
|
+
const hasData = !!data && Object.keys(data).length > 0;
|
|
131
|
+
const hasStorage = !!storage && typeof storage === 'object' && Object.keys(storage).length > 0;
|
|
132
|
+
return hasData || hasStorage;
|
|
133
|
+
}
|
|
134
|
+
function checkHeaders({ manifest, capabilities, tooling, cliVersion, sdkVersion, issues, }) {
|
|
135
|
+
const feature = capabilities?.features.http_headers;
|
|
136
|
+
if (!feature || !needsWideHeaders(manifest))
|
|
137
|
+
return;
|
|
138
|
+
const supported = capabilities_1.MANAGED_FUNCTION_CAPABILITIES.features.http_headers.max_bytes;
|
|
139
|
+
if (feature.max_bytes >= supported)
|
|
140
|
+
return;
|
|
141
|
+
issues.push(issue({
|
|
142
|
+
field: feature.field,
|
|
143
|
+
problem: `invocation headers for capabilities.data or capabilities.storage exceed ${feature.max_bytes} bytes and will fail with HTTP 431`,
|
|
144
|
+
limit: feature.limit,
|
|
145
|
+
tooling,
|
|
146
|
+
requiredCli: feature.min_cli,
|
|
147
|
+
requiredSdk: feature.min_sdk,
|
|
148
|
+
cliVersion,
|
|
149
|
+
sdkVersion,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
function checkSdkTimeout({ manifest, limits, capabilities, tooling, cliVersion, sdkVersion, issues, }) {
|
|
153
|
+
const timeout = manifest.limits?.timeout_seconds;
|
|
154
|
+
if (typeof timeout !== 'number' || !sdkVersion)
|
|
155
|
+
return;
|
|
156
|
+
const feature = capabilities?.features.invoke_timeout ?? capabilities_1.MANAGED_FUNCTION_CAPABILITIES.features.invoke_timeout;
|
|
157
|
+
if (!(0, sdk_range_1.sdkConstraintIsOlder)(sdkVersion, feature.min_sdk))
|
|
158
|
+
return;
|
|
159
|
+
if (timeout <= feature.legacy_sdk_max_seconds)
|
|
160
|
+
return;
|
|
161
|
+
const platformMax = numericCap(limits?.workload?.timeout_seconds)?.max ?? feature.max_seconds;
|
|
162
|
+
issues.push(issue({
|
|
163
|
+
field: feature.field,
|
|
164
|
+
problem: `${timeout} seconds exceeds the installed SDK's ${feature.legacy_sdk_max_seconds} second client cap, behind the platform limit of ${platformMax} seconds`,
|
|
165
|
+
limit: feature.limit,
|
|
166
|
+
tooling,
|
|
167
|
+
requiredCli: feature.min_cli,
|
|
168
|
+
requiredSdk: feature.min_sdk,
|
|
169
|
+
cliVersion,
|
|
170
|
+
sdkVersion,
|
|
171
|
+
}));
|
|
172
|
+
}
|