@hypequery/deployment 0.8.0 → 0.9.1
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 +5 -31
- package/dist/activation.d.ts.map +1 -1
- package/dist/activation.js +2 -1
- package/dist/authorized-contract.d.ts +20 -49
- package/dist/authorized-contract.d.ts.map +1 -1
- package/dist/authorized-contract.js +0 -0
- package/dist/bundle.d.ts.map +1 -1
- package/dist/bundle.js +10 -36
- package/dist/control-plane-limits.js +1 -1
- package/dist/filesystem-store.d.ts.map +1 -1
- package/dist/filesystem-store.js +7 -22
- package/dist/index.d.ts +1 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -10
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +8 -5
- package/dist/limits.d.ts.map +1 -1
- package/dist/limits.js +5 -2
- package/dist/principal.d.ts +11 -0
- package/dist/principal.d.ts.map +1 -0
- package/dist/principal.js +1 -0
- package/dist/semantic-data-plane.d.ts +13 -17
- package/dist/semantic-data-plane.d.ts.map +1 -1
- package/dist/semantic-data-plane.js +30 -20
- package/dist/semantic-operation-validation.d.ts +2 -2
- package/dist/semantic-operation-validation.d.ts.map +1 -1
- package/dist/semantic-operation-validation.js +16 -36
- package/dist/utils/read-bounded-file.d.ts +4 -0
- package/dist/utils/read-bounded-file.d.ts.map +1 -0
- package/dist/utils/read-bounded-file.js +18 -0
- package/package.json +4 -4
- package/dist/data-plane-adapters.d.ts +0 -18
- package/dist/data-plane-adapters.d.ts.map +0 -1
- package/dist/data-plane-adapters.js +0 -304
- package/dist/data-plane-limits.d.ts +0 -5
- package/dist/data-plane-limits.d.ts.map +0 -1
- package/dist/data-plane-limits.js +0 -5
- package/dist/data-plane-runtime.d.ts +0 -13
- package/dist/data-plane-runtime.d.ts.map +0 -1
- package/dist/data-plane-runtime.js +0 -12
- package/dist/data-plane.d.ts +0 -84
- package/dist/data-plane.d.ts.map +0 -1
- package/dist/data-plane.js +0 -209
- package/dist/filesystem-host.d.ts +0 -51
- package/dist/filesystem-host.d.ts.map +0 -1
- package/dist/filesystem-host.js +0 -73
- package/dist/host.d.ts +0 -40
- package/dist/host.d.ts.map +0 -1
- package/dist/host.js +0 -183
- package/dist/node-runtime-factory.d.ts +0 -27
- package/dist/node-runtime-factory.d.ts.map +0 -1
- package/dist/node-runtime-factory.js +0 -336
- package/dist/runtime-materialization.d.ts +0 -58
- package/dist/runtime-materialization.d.ts.map +0 -1
- package/dist/runtime-materialization.js +0 -187
- package/dist/runtime-supervisor.d.ts +0 -77
- package/dist/runtime-supervisor.d.ts.map +0 -1
- package/dist/runtime-supervisor.js +0 -265
|
@@ -1,10 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Dataset invocation against an activated deployment.
|
|
3
|
+
*
|
|
4
|
+
* Decision 0002 requires a dataset call to run a fixed enforcement sequence:
|
|
5
|
+
* select the active generation, resolve the target from its validated
|
|
6
|
+
* contract, authenticate, enforce roles and scopes, resolve tenant, apply the
|
|
7
|
+
* most restrictive budget, validate input, execute, then validate and bound
|
|
8
|
+
* the output.
|
|
9
|
+
*
|
|
10
|
+
* Execution itself is injected. This module decides whether a call is allowed
|
|
11
|
+
* and what it is allowed to ask for.
|
|
12
|
+
*/
|
|
6
13
|
import { ProtocolSemanticInvocationError, validateProtocolDeploymentContract, validateProtocolSemanticInvocation, validateProtocolSemanticInvocationResult, } from '@hypequery/protocol';
|
|
7
14
|
import { validateSemanticOperation, } from './semantic-operation-validation.js';
|
|
15
|
+
import { DeploymentSemanticInvocationError, fail, throwIfAborted } from './semantic-invocation-errors.js';
|
|
16
|
+
import { missing } from './utils/required-access.js';
|
|
17
|
+
import { definedLimits, lowest, tighten } from './utils/semantic-budget-limits.js';
|
|
18
|
+
import { claimedFailure } from './utils/semantic-executor-failure.js';
|
|
19
|
+
export { DeploymentSemanticInvocationError, toProtocolSemanticInvocationFailure } from './semantic-invocation-errors.js';
|
|
8
20
|
const DEFAULT_LIMITS = Object.freeze({
|
|
9
21
|
maxRows: 10_000,
|
|
10
22
|
maxOffset: 10_000,
|
|
@@ -30,6 +42,14 @@ export function createDeploymentSemanticDataPlane(options) {
|
|
|
30
42
|
// against everything. A caller that omits a limit gets the default.
|
|
31
43
|
const configured = { ...DEFAULT_LIMITS, ...definedLimits(options.limits) };
|
|
32
44
|
function resolveTarget(operation) {
|
|
45
|
+
// A metric target cannot be served: the contract has no field that could
|
|
46
|
+
// carry one, so there is nothing to look up and nothing to authorize.
|
|
47
|
+
if (operation.kind === 'metric') {
|
|
48
|
+
fail('not-found', 'HQ_SEMANTIC_METRIC_NOT_FOUND', 'The metric was not found.', {
|
|
49
|
+
path: '$.operation.metric',
|
|
50
|
+
relist: true,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
33
53
|
const dataset = datasets.get(String(operation.dataset));
|
|
34
54
|
if (dataset === undefined) {
|
|
35
55
|
fail('not-found', 'HQ_SEMANTIC_DATASET_NOT_FOUND', 'The dataset was not found.', {
|
|
@@ -37,25 +57,16 @@ export function createDeploymentSemanticDataPlane(options) {
|
|
|
37
57
|
relist: true,
|
|
38
58
|
});
|
|
39
59
|
}
|
|
40
|
-
const metric = operation.kind === 'metric'
|
|
41
|
-
? dataset.metrics.find(entry => String(entry.name) === String(operation.metric))
|
|
42
|
-
: undefined;
|
|
43
|
-
if (operation.kind === 'metric' && metric === undefined) {
|
|
44
|
-
fail('not-found', 'HQ_SEMANTIC_METRIC_NOT_FOUND', 'The metric was not found.', {
|
|
45
|
-
path: '$.operation.metric',
|
|
46
|
-
relist: true,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
60
|
// An endpoint policy is what publishes a target. Without one the contract
|
|
50
61
|
// describes the dataset but never exposed it, so it is not addressable.
|
|
51
|
-
const endpoint =
|
|
62
|
+
const endpoint = dataset.endpoint;
|
|
52
63
|
if (endpoint === undefined) {
|
|
53
64
|
fail('not-found', 'HQ_SEMANTIC_NOT_PUBLISHED', 'The target is not published.', {
|
|
54
65
|
path: '$.operation',
|
|
55
66
|
relist: true,
|
|
56
67
|
});
|
|
57
68
|
}
|
|
58
|
-
return { dataset,
|
|
69
|
+
return { dataset, endpoint };
|
|
59
70
|
}
|
|
60
71
|
async function invoke(request) {
|
|
61
72
|
throwIfAborted(request.signal);
|
|
@@ -76,7 +87,7 @@ export function createDeploymentSemanticDataPlane(options) {
|
|
|
76
87
|
fail('stale-activation', 'HQ_SEMANTIC_STALE_ACTIVATION', 'The pinned activation is no longer active.', { retryable: true, relist: true });
|
|
77
88
|
}
|
|
78
89
|
const operation = invocation.operation;
|
|
79
|
-
const { dataset,
|
|
90
|
+
const { dataset, endpoint } = resolveTarget(operation);
|
|
80
91
|
let principal = null;
|
|
81
92
|
if (endpoint.access.kind === 'authenticated' && !options.authenticate) {
|
|
82
93
|
fail('configuration-invalid', 'HQ_SEMANTIC_CONFIGURATION', 'An authenticator is required for this target.');
|
|
@@ -85,7 +96,7 @@ export function createDeploymentSemanticDataPlane(options) {
|
|
|
85
96
|
&& (endpoint.access.kind === 'authenticated' || request.credentials !== undefined)) {
|
|
86
97
|
try {
|
|
87
98
|
principal = await options.authenticate({
|
|
88
|
-
credentials: request.credentials, invocation, dataset,
|
|
99
|
+
credentials: request.credentials, invocation, dataset,
|
|
89
100
|
});
|
|
90
101
|
}
|
|
91
102
|
catch (error) {
|
|
@@ -112,7 +123,7 @@ export function createDeploymentSemanticDataPlane(options) {
|
|
|
112
123
|
else {
|
|
113
124
|
try {
|
|
114
125
|
tenant = await options.resolveTenant({
|
|
115
|
-
principal, invocation, dataset,
|
|
126
|
+
principal, invocation, dataset,
|
|
116
127
|
});
|
|
117
128
|
}
|
|
118
129
|
catch (error) {
|
|
@@ -157,7 +168,6 @@ export function createDeploymentSemanticDataPlane(options) {
|
|
|
157
168
|
output = await options.execute({
|
|
158
169
|
deployment,
|
|
159
170
|
dataset,
|
|
160
|
-
...(metric === undefined ? {} : { metric }),
|
|
161
171
|
operation,
|
|
162
172
|
principal,
|
|
163
173
|
tenant,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* loading the authoring package. A gateway must be able to reject a bad
|
|
7
7
|
* invocation without any of the deploying application's code.
|
|
8
8
|
*/
|
|
9
|
-
import type {
|
|
9
|
+
import type { ProtocolDeploymentDataset, ProtocolSemanticQuery } from '@hypequery/protocol';
|
|
10
10
|
export interface SemanticOperationViolation {
|
|
11
11
|
readonly message: string;
|
|
12
12
|
readonly path: string;
|
|
@@ -25,5 +25,5 @@ export interface SemanticOperationLimits {
|
|
|
25
25
|
* Returns every violation rather than the first, so a caller correcting an
|
|
26
26
|
* agent-authored query sees the whole problem in one round trip.
|
|
27
27
|
*/
|
|
28
|
-
export declare function validateSemanticOperation(operation: ProtocolSemanticQuery, dataset:
|
|
28
|
+
export declare function validateSemanticOperation(operation: ProtocolSemanticQuery, dataset: ProtocolDeploymentDataset, datasets: ReadonlyMap<string, ProtocolDeploymentDataset>, limits: SemanticOperationLimits): readonly SemanticOperationViolation[];
|
|
29
29
|
//# sourceMappingURL=semantic-operation-validation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-operation-validation.d.ts","sourceRoot":"","sources":["../src/semantic-operation-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"semantic-operation-validation.d.ts","sourceRoot":"","sources":["../src/semantic-operation-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,yBAAyB,EAEzB,qBAAqB,EAEtB,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,+EAA+E;IAC/E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AA8ED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,qBAAqB,EAChC,OAAO,EAAE,yBAAyB,EAClC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,yBAAyB,CAAC,EACxD,MAAM,EAAE,uBAAuB,GAC9B,SAAS,0BAA0B,EAAE,CA0FvC"}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
const ALL_GRAINS = ['day', 'week', 'month', 'quarter', 'year'];
|
|
10
10
|
/**
|
|
11
|
-
* What a dataset exposes
|
|
11
|
+
* What a dataset exposes.
|
|
12
12
|
*
|
|
13
13
|
* A relationship contributes `<name>.<dimension>` for its target's groupable
|
|
14
14
|
* dimensions, matching the one-hop rule the authoring layer enforces. Filters
|
|
@@ -52,20 +52,6 @@ function resolveDataset(dataset, datasets) {
|
|
|
52
52
|
grains: new Set(dataset.timeField === undefined ? [] : ALL_GRAINS),
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
-
/** A metric may only narrow what its dataset exposes, never widen it. */
|
|
56
|
-
function narrowToMetric(base, metric) {
|
|
57
|
-
const declaredDimensions = new Set(metric.dimensions.map(String));
|
|
58
|
-
const declaredFilters = new Set(metric.filters.map(String));
|
|
59
|
-
return {
|
|
60
|
-
groupable: new Set([...base.groupable].filter(name => declaredDimensions.has(name))),
|
|
61
|
-
filters: new Map([...base.filters].filter(([name]) => declaredFilters.has(name))),
|
|
62
|
-
// A metric selects itself; a caller cannot add measures to it.
|
|
63
|
-
measures: new Set(),
|
|
64
|
-
grains: new Set(metric.grain === undefined
|
|
65
|
-
? metric.grains.filter(grain => base.grains.has(grain))
|
|
66
|
-
: [metric.grain]),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
55
|
/**
|
|
70
56
|
* The field a filter expression addresses, or null when it is not a plain
|
|
71
57
|
* comparison.
|
|
@@ -93,16 +79,13 @@ function comparisonField(expression) {
|
|
|
93
79
|
export function validateSemanticOperation(operation, dataset, datasets, limits) {
|
|
94
80
|
const violations = [];
|
|
95
81
|
const fail = (message, path) => violations.push({ message, path });
|
|
96
|
-
|
|
82
|
+
// A deployment carries no metric, so a metric operation addresses nothing the
|
|
83
|
+
// contract published, whatever else it selects.
|
|
97
84
|
if (operation.kind === 'metric') {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
fail(`Unknown metric "${String(operation.metric)}".`, '$.operation.metric');
|
|
101
|
-
return violations;
|
|
102
|
-
}
|
|
85
|
+
fail(`Unknown metric "${String(operation.metric)}".`, '$.operation.metric');
|
|
86
|
+
return violations;
|
|
103
87
|
}
|
|
104
|
-
const
|
|
105
|
-
const allowed = metric === undefined ? base : narrowToMetric(base, metric);
|
|
88
|
+
const allowed = resolveDataset(dataset, datasets);
|
|
106
89
|
const dimensions = operation.dimensions ?? [];
|
|
107
90
|
if (dimensions.length > limits.maxDimensions) {
|
|
108
91
|
fail(`At most ${limits.maxDimensions} dimensions may be selected.`, '$.operation.dimensions');
|
|
@@ -115,19 +98,17 @@ export function validateSemanticOperation(operation, dataset, datasets, limits)
|
|
|
115
98
|
if (new Set(dimensions.map(String)).size !== dimensions.length) {
|
|
116
99
|
fail('Dimensions must be unique.', '$.operation.dimensions');
|
|
117
100
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
measures.
|
|
124
|
-
|
|
125
|
-
fail(`Unknown measure "${String(name)}".`, `$.operation.measures[${index}]`);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
if (dimensions.length === 0 && measures.length === 0) {
|
|
129
|
-
fail('At least one dimension or measure must be selected.', '$.operation');
|
|
101
|
+
const measures = operation.measures ?? [];
|
|
102
|
+
if (measures.length > limits.maxMeasures) {
|
|
103
|
+
fail(`At most ${limits.maxMeasures} measures may be selected.`, '$.operation.measures');
|
|
104
|
+
}
|
|
105
|
+
measures.forEach((name, index) => {
|
|
106
|
+
if (!allowed.measures.has(String(name))) {
|
|
107
|
+
fail(`Unknown measure "${String(name)}".`, `$.operation.measures[${index}]`);
|
|
130
108
|
}
|
|
109
|
+
});
|
|
110
|
+
if (dimensions.length === 0 && measures.length === 0) {
|
|
111
|
+
fail('At least one dimension or measure must be selected.', '$.operation');
|
|
131
112
|
}
|
|
132
113
|
const filters = operation.filters ?? [];
|
|
133
114
|
if (filters.length > limits.maxFilters) {
|
|
@@ -156,7 +137,6 @@ export function validateSemanticOperation(operation, dataset, datasets, limits)
|
|
|
156
137
|
const orderable = new Set([
|
|
157
138
|
...allowed.groupable,
|
|
158
139
|
...allowed.measures,
|
|
159
|
-
...(metric === undefined ? [] : [String(metric.name)]),
|
|
160
140
|
...(operation.by === undefined ? [] : ['period']),
|
|
161
141
|
]);
|
|
162
142
|
orderBy.forEach((entry, index) => {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FileHandle } from 'node:fs/promises';
|
|
2
|
+
/** Read at most the declared limit, including when a file grows after stat. */
|
|
3
|
+
export declare function readBoundedFile(handle: FileHandle, maximum: number, minimum: number, errorMessage: string): Promise<Buffer>;
|
|
4
|
+
//# sourceMappingURL=read-bounded-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-bounded-file.d.ts","sourceRoot":"","sources":["../../src/utils/read-bounded-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD,+EAA+E;AAC/E,wBAAsB,eAAe,CACnC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAajB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Read at most the declared limit, including when a file grows after stat. */
|
|
2
|
+
export async function readBoundedFile(handle, maximum, minimum, errorMessage) {
|
|
3
|
+
const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, maximum + 1));
|
|
4
|
+
const chunks = [];
|
|
5
|
+
let total = 0;
|
|
6
|
+
while (total <= maximum) {
|
|
7
|
+
const { bytesRead } = await handle.read(buffer, 0, Math.min(buffer.length, maximum + 1 - total), null);
|
|
8
|
+
if (bytesRead === 0)
|
|
9
|
+
break;
|
|
10
|
+
total += bytesRead;
|
|
11
|
+
if (total > maximum)
|
|
12
|
+
throw new Error(errorMessage);
|
|
13
|
+
chunks.push(Buffer.from(buffer.subarray(0, bytesRead)));
|
|
14
|
+
}
|
|
15
|
+
if (total < minimum)
|
|
16
|
+
throw new Error(errorMessage);
|
|
17
|
+
return Buffer.concat(chunks, total);
|
|
18
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/deployment",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Verified deployment intake, activation, and
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Verified deployment intake, activation, and semantic invocation for Hypequery analytics",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hypequery",
|
|
7
7
|
"deployment",
|
|
@@ -26,13 +26,13 @@
|
|
|
26
26
|
"README.md"
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@hypequery/protocol": "0.
|
|
29
|
+
"@hypequery/protocol": "0.15.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@types/node": "^22.5.0",
|
|
33
33
|
"typescript": "^5.7.3",
|
|
34
34
|
"vitest": "^3.2.6",
|
|
35
|
-
"@hypequery/datasets": "0.
|
|
35
|
+
"@hypequery/datasets": "0.18.0"
|
|
36
36
|
},
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=20"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
-
import { type DeploymentDataPlane } from './data-plane.js';
|
|
3
|
-
export type DeploymentDataPlaneFetchHandler = (request: Request) => Promise<Response>;
|
|
4
|
-
export type DeploymentDataPlaneNodeHandler = (request: IncomingMessage, response: ServerResponse) => Promise<void>;
|
|
5
|
-
export interface DeploymentDataPlaneAdapterRequest {
|
|
6
|
-
readonly method: string;
|
|
7
|
-
readonly path: string;
|
|
8
|
-
readonly query: Readonly<Record<string, string | readonly string[]>>;
|
|
9
|
-
readonly headers: Readonly<Record<string, string>>;
|
|
10
|
-
}
|
|
11
|
-
export interface DeploymentDataPlaneAdapterOptions {
|
|
12
|
-
/** Request body limit from 1 through 1,048,576 bytes. */
|
|
13
|
-
readonly maxRequestBytes?: number;
|
|
14
|
-
readonly credentials?: (request: DeploymentDataPlaneAdapterRequest) => unknown;
|
|
15
|
-
}
|
|
16
|
-
export declare function createDeploymentDataPlaneFetchHandler(dataPlane: DeploymentDataPlane, options?: DeploymentDataPlaneAdapterOptions): DeploymentDataPlaneFetchHandler;
|
|
17
|
-
export declare function createDeploymentDataPlaneNodeHandler(dataPlane: DeploymentDataPlane, options?: DeploymentDataPlaneAdapterOptions): DeploymentDataPlaneNodeHandler;
|
|
18
|
-
//# sourceMappingURL=data-plane-adapters.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-plane-adapters.d.ts","sourceRoot":"","sources":["../src/data-plane-adapters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAIjE,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,iBAAiB,CAAC;AAGzB,MAAM,MAAM,+BAA+B,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AACtF,MAAM,MAAM,8BAA8B,GAAG,CAC3C,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,KACrB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;IACrE,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,iCAAiC;IAChD,yDAAyD;IACzD,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,iCAAiC,KAAK,OAAO,CAAC;CAChF;AAoPD,wBAAgB,qCAAqC,CACnD,SAAS,EAAE,mBAAmB,EAC9B,OAAO,GAAE,iCAAsC,GAC9C,+BAA+B,CAwBjC;AAqDD,wBAAgB,oCAAoC,CAClD,SAAS,EAAE,mBAAmB,EAC9B,OAAO,GAAE,iCAAsC,GAC9C,8BAA8B,CAuChC"}
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_PROTOCOL_SCHEMA_VALUE_LIMITS, } from '@hypequery/protocol';
|
|
2
|
-
import { DeploymentDataPlaneError, } from './data-plane.js';
|
|
3
|
-
import { DeploymentHostError } from './host.js';
|
|
4
|
-
class AdapterError extends Error {
|
|
5
|
-
code;
|
|
6
|
-
status;
|
|
7
|
-
constructor(code, status, message) {
|
|
8
|
-
super(message);
|
|
9
|
-
this.code = code;
|
|
10
|
-
this.status = status;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
const JSON_CONTENT_TYPE = /^application\/json(?:;\s*charset=utf-8)?$/i;
|
|
14
|
-
function maximumBytes(input) {
|
|
15
|
-
const maximum = DEFAULT_PROTOCOL_SCHEMA_VALUE_LIMITS.maxInputBytes;
|
|
16
|
-
const value = input ?? maximum;
|
|
17
|
-
if (!Number.isSafeInteger(value) || value < 1 || value > maximum) {
|
|
18
|
-
throw new RangeError(`maxRequestBytes must be an integer between 1 and ${maximum}`);
|
|
19
|
-
}
|
|
20
|
-
return value;
|
|
21
|
-
}
|
|
22
|
-
function queryParameters(search) {
|
|
23
|
-
const query = Object.create(null);
|
|
24
|
-
for (const [name, value] of search) {
|
|
25
|
-
const current = query[name];
|
|
26
|
-
if (current === undefined)
|
|
27
|
-
query[name] = value;
|
|
28
|
-
else if (typeof current === 'string')
|
|
29
|
-
query[name] = Object.freeze([current, value]);
|
|
30
|
-
else
|
|
31
|
-
query[name] = Object.freeze([...current, value]);
|
|
32
|
-
}
|
|
33
|
-
return Object.freeze(query);
|
|
34
|
-
}
|
|
35
|
-
function contentLength(headers, maximum) {
|
|
36
|
-
const value = headers['content-length'];
|
|
37
|
-
if (value === undefined)
|
|
38
|
-
return undefined;
|
|
39
|
-
if (!/^(?:0|[1-9][0-9]*)$/.test(value)) {
|
|
40
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Content-Length is invalid.');
|
|
41
|
-
}
|
|
42
|
-
const length = Number(value);
|
|
43
|
-
if (!Number.isSafeInteger(length)) {
|
|
44
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Content-Length is invalid.');
|
|
45
|
-
}
|
|
46
|
-
if (length > maximum) {
|
|
47
|
-
throw new AdapterError('HQ_DATA_PLANE_REQUEST_TOO_LARGE', 413, 'The data-plane request exceeds its byte limit.');
|
|
48
|
-
}
|
|
49
|
-
return length;
|
|
50
|
-
}
|
|
51
|
-
function requestInput(query, body, contentType) {
|
|
52
|
-
const hasQuery = Object.keys(query).length > 0;
|
|
53
|
-
if (body !== undefined && hasQuery) {
|
|
54
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'A data-plane request cannot contain both query parameters and a JSON body.');
|
|
55
|
-
}
|
|
56
|
-
if (body !== undefined) {
|
|
57
|
-
if (!contentType || !JSON_CONTENT_TYPE.test(contentType)) {
|
|
58
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Data-plane request bodies require application/json with UTF-8 encoding.');
|
|
59
|
-
}
|
|
60
|
-
return body;
|
|
61
|
-
}
|
|
62
|
-
return hasQuery ? JSON.stringify(query) : undefined;
|
|
63
|
-
}
|
|
64
|
-
function credentials(options, request) {
|
|
65
|
-
return options.credentials ? options.credentials(request) : request.headers.authorization;
|
|
66
|
-
}
|
|
67
|
-
function cacheControl(result) {
|
|
68
|
-
if (result.cacheTtlMs === undefined || result.cacheTtlMs <= 0)
|
|
69
|
-
return 'no-store';
|
|
70
|
-
return `public, max-age=${Math.floor(result.cacheTtlMs / 1_000)}`;
|
|
71
|
-
}
|
|
72
|
-
function successResponse(result) {
|
|
73
|
-
if (result.output === undefined) {
|
|
74
|
-
return Object.freeze({
|
|
75
|
-
status: 204,
|
|
76
|
-
headers: Object.freeze({ 'cache-control': cacheControl(result) }),
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
const body = `${JSON.stringify(result.output)}\n`;
|
|
80
|
-
return Object.freeze({
|
|
81
|
-
status: 200,
|
|
82
|
-
headers: Object.freeze({
|
|
83
|
-
'content-type': 'application/json; charset=utf-8',
|
|
84
|
-
'content-length': String(Buffer.byteLength(body)),
|
|
85
|
-
'cache-control': cacheControl(result),
|
|
86
|
-
}),
|
|
87
|
-
body,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
function errorStatus(error) {
|
|
91
|
-
switch (error.code) {
|
|
92
|
-
case 'HQ_DATA_PLANE_INPUT_INVALID': return 400;
|
|
93
|
-
case 'HQ_DATA_PLANE_UNAUTHENTICATED': return 401;
|
|
94
|
-
case 'HQ_DATA_PLANE_FORBIDDEN':
|
|
95
|
-
case 'HQ_DATA_PLANE_TENANT_REQUIRED': return 403;
|
|
96
|
-
case 'HQ_DATA_PLANE_ROUTE_NOT_FOUND': return 404;
|
|
97
|
-
case 'HQ_DATA_PLANE_METHOD_NOT_ALLOWED': return 405;
|
|
98
|
-
case 'HQ_DATA_PLANE_ABORTED': return 499;
|
|
99
|
-
case 'HQ_DATA_PLANE_EXECUTOR_UNAVAILABLE': return 503;
|
|
100
|
-
default: return 500;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function errorResponse(error) {
|
|
104
|
-
let status = 500;
|
|
105
|
-
let code = 'HQ_DATA_PLANE_INTERNAL';
|
|
106
|
-
let message = 'The deployment data-plane request could not be processed.';
|
|
107
|
-
let path;
|
|
108
|
-
if (error instanceof AdapterError) {
|
|
109
|
-
status = error.status;
|
|
110
|
-
code = error.code;
|
|
111
|
-
message = error.message;
|
|
112
|
-
}
|
|
113
|
-
else if (error instanceof DeploymentDataPlaneError) {
|
|
114
|
-
status = errorStatus(error);
|
|
115
|
-
code = error.code;
|
|
116
|
-
if (status < 500) {
|
|
117
|
-
message = error.message;
|
|
118
|
-
path = error.path;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
else if (error instanceof DeploymentHostError) {
|
|
122
|
-
code = error.code;
|
|
123
|
-
if (error.code === 'HQ_DEPLOYMENT_HOST_NOT_READY'
|
|
124
|
-
|| error.code === 'HQ_DEPLOYMENT_HOST_CLOSED') {
|
|
125
|
-
status = 503;
|
|
126
|
-
message = error.message;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
const body = `${JSON.stringify({
|
|
130
|
-
error: { code, message, ...(path === undefined ? {} : { path }) },
|
|
131
|
-
})}\n`;
|
|
132
|
-
return Object.freeze({
|
|
133
|
-
status,
|
|
134
|
-
headers: Object.freeze({
|
|
135
|
-
'content-type': 'application/json; charset=utf-8',
|
|
136
|
-
'content-length': String(Buffer.byteLength(body)),
|
|
137
|
-
'cache-control': 'no-store',
|
|
138
|
-
...(status === 401 ? { 'www-authenticate': 'Bearer' } : {}),
|
|
139
|
-
}),
|
|
140
|
-
body,
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
async function execute(dataPlane, options, request, input, signal) {
|
|
144
|
-
try {
|
|
145
|
-
const result = await dataPlane.executeJson({
|
|
146
|
-
...request,
|
|
147
|
-
input,
|
|
148
|
-
credentials: credentials(options, request),
|
|
149
|
-
signal,
|
|
150
|
-
});
|
|
151
|
-
return successResponse(result);
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
return errorResponse(error);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
function fetchHeaders(input) {
|
|
158
|
-
const headers = Object.create(null);
|
|
159
|
-
for (const [name, value] of input)
|
|
160
|
-
headers[name.toLowerCase()] = value;
|
|
161
|
-
if (headers.authorization?.includes(',')) {
|
|
162
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Authorization header is ambiguous.');
|
|
163
|
-
}
|
|
164
|
-
return Object.freeze(headers);
|
|
165
|
-
}
|
|
166
|
-
async function fetchBytes(stream, declared, maximum) {
|
|
167
|
-
if (stream === null) {
|
|
168
|
-
if (declared !== undefined && declared !== 0) {
|
|
169
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Request body length is invalid.');
|
|
170
|
-
}
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
const reader = stream.getReader();
|
|
174
|
-
const chunks = [];
|
|
175
|
-
let total = 0;
|
|
176
|
-
try {
|
|
177
|
-
while (true) {
|
|
178
|
-
const result = await reader.read();
|
|
179
|
-
if (result.done)
|
|
180
|
-
break;
|
|
181
|
-
total += result.value.byteLength;
|
|
182
|
-
if (total > maximum)
|
|
183
|
-
throw new AdapterError('HQ_DATA_PLANE_REQUEST_TOO_LARGE', 413, 'The data-plane request exceeds its byte limit.');
|
|
184
|
-
if (declared !== undefined && total > declared) {
|
|
185
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Request body length is invalid.');
|
|
186
|
-
}
|
|
187
|
-
chunks.push(result.value);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
await reader.cancel(error).catch(() => undefined);
|
|
192
|
-
throw error;
|
|
193
|
-
}
|
|
194
|
-
finally {
|
|
195
|
-
reader.releaseLock();
|
|
196
|
-
}
|
|
197
|
-
if (declared !== undefined && total !== declared) {
|
|
198
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Request body length is invalid.');
|
|
199
|
-
}
|
|
200
|
-
const bytes = new Uint8Array(total);
|
|
201
|
-
let offset = 0;
|
|
202
|
-
for (const chunk of chunks) {
|
|
203
|
-
bytes.set(chunk, offset);
|
|
204
|
-
offset += chunk.byteLength;
|
|
205
|
-
}
|
|
206
|
-
return bytes.byteLength === 0 ? undefined : bytes;
|
|
207
|
-
}
|
|
208
|
-
export function createDeploymentDataPlaneFetchHandler(dataPlane, options = {}) {
|
|
209
|
-
const maximum = maximumBytes(options.maxRequestBytes);
|
|
210
|
-
return async (request) => {
|
|
211
|
-
let response;
|
|
212
|
-
try {
|
|
213
|
-
const url = new URL(request.url);
|
|
214
|
-
const headers = fetchHeaders(request.headers);
|
|
215
|
-
const query = queryParameters(url.searchParams);
|
|
216
|
-
const body = await fetchBytes(request.body, contentLength(headers, maximum), maximum);
|
|
217
|
-
const adapted = Object.freeze({
|
|
218
|
-
method: request.method.toUpperCase(), path: url.pathname, query, headers,
|
|
219
|
-
});
|
|
220
|
-
response = await execute(dataPlane, options, adapted, requestInput(query, body, headers['content-type']), request.signal);
|
|
221
|
-
}
|
|
222
|
-
catch (error) {
|
|
223
|
-
response = errorResponse(error);
|
|
224
|
-
}
|
|
225
|
-
return new Response(response.body ?? null, { status: response.status, headers: response.headers });
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
function nodeHeaders(request) {
|
|
229
|
-
const headers = Object.create(null);
|
|
230
|
-
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
231
|
-
const name = request.rawHeaders[index].toLowerCase();
|
|
232
|
-
const value = request.rawHeaders[index + 1];
|
|
233
|
-
if (name === 'authorization' && headers[name] !== undefined) {
|
|
234
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Authorization header is ambiguous.');
|
|
235
|
-
}
|
|
236
|
-
headers[name] = headers[name] === undefined ? value : `${headers[name]}, ${value}`;
|
|
237
|
-
}
|
|
238
|
-
return Object.freeze(headers);
|
|
239
|
-
}
|
|
240
|
-
function nodeHasBody(headers) {
|
|
241
|
-
return headers['transfer-encoding'] !== undefined
|
|
242
|
-
|| headers['content-length'] !== undefined && headers['content-length'] !== '0';
|
|
243
|
-
}
|
|
244
|
-
async function nodeBytes(request, hasBody, declared, maximum) {
|
|
245
|
-
if (!hasBody)
|
|
246
|
-
return undefined;
|
|
247
|
-
const chunks = [];
|
|
248
|
-
let total = 0;
|
|
249
|
-
for await (const chunk of request) {
|
|
250
|
-
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
251
|
-
total += bytes.byteLength;
|
|
252
|
-
if (total > maximum)
|
|
253
|
-
throw new AdapterError('HQ_DATA_PLANE_REQUEST_TOO_LARGE', 413, 'The data-plane request exceeds its byte limit.');
|
|
254
|
-
if (declared !== undefined && total > declared) {
|
|
255
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Request body length is invalid.');
|
|
256
|
-
}
|
|
257
|
-
chunks.push(bytes);
|
|
258
|
-
}
|
|
259
|
-
if (declared !== undefined && total !== declared) {
|
|
260
|
-
throw new AdapterError('HQ_DATA_PLANE_INPUT_INVALID', 400, 'Request body length is invalid.');
|
|
261
|
-
}
|
|
262
|
-
return total === 0 ? undefined : Buffer.concat(chunks, total);
|
|
263
|
-
}
|
|
264
|
-
function sendNodeResponse(response, result) {
|
|
265
|
-
if (response.writableEnded || response.headersSent)
|
|
266
|
-
return;
|
|
267
|
-
response.statusCode = result.status;
|
|
268
|
-
for (const [name, value] of Object.entries(result.headers))
|
|
269
|
-
response.setHeader(name, value);
|
|
270
|
-
response.end(result.body);
|
|
271
|
-
}
|
|
272
|
-
export function createDeploymentDataPlaneNodeHandler(dataPlane, options = {}) {
|
|
273
|
-
const maximum = maximumBytes(options.maxRequestBytes);
|
|
274
|
-
return async (request, response) => {
|
|
275
|
-
const abort = new AbortController();
|
|
276
|
-
const onRequestClose = () => {
|
|
277
|
-
if (!request.complete)
|
|
278
|
-
abort.abort(new Error('The data-plane request was interrupted.'));
|
|
279
|
-
};
|
|
280
|
-
const onResponseClose = () => {
|
|
281
|
-
if (!response.writableEnded)
|
|
282
|
-
abort.abort(new Error('The data-plane response was interrupted.'));
|
|
283
|
-
};
|
|
284
|
-
request.once('close', onRequestClose);
|
|
285
|
-
response.once('close', onResponseClose);
|
|
286
|
-
try {
|
|
287
|
-
const url = new URL(request.url ?? '/', 'http://deployment-data-plane.invalid');
|
|
288
|
-
const headers = nodeHeaders(request);
|
|
289
|
-
const query = queryParameters(url.searchParams);
|
|
290
|
-
const body = await nodeBytes(request, nodeHasBody(headers), contentLength(headers, maximum), maximum);
|
|
291
|
-
const adapted = Object.freeze({
|
|
292
|
-
method: (request.method ?? 'GET').toUpperCase(), path: url.pathname, query, headers,
|
|
293
|
-
});
|
|
294
|
-
sendNodeResponse(response, await execute(dataPlane, options, adapted, requestInput(query, body, headers['content-type']), abort.signal));
|
|
295
|
-
}
|
|
296
|
-
catch (error) {
|
|
297
|
-
sendNodeResponse(response, errorResponse(error));
|
|
298
|
-
}
|
|
299
|
-
finally {
|
|
300
|
-
request.off('close', onRequestClose);
|
|
301
|
-
response.off('close', onResponseClose);
|
|
302
|
-
}
|
|
303
|
-
};
|
|
304
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { type ProtocolSchemaValueLimits } from '@hypequery/protocol';
|
|
2
|
-
export type DeploymentDataPlaneLimits = ProtocolSchemaValueLimits;
|
|
3
|
-
export declare const DEFAULT_DEPLOYMENT_DATA_PLANE_LIMITS: Readonly<ProtocolSchemaValueLimits>;
|
|
4
|
-
export declare function resolveDeploymentDataPlaneLimits(input?: Partial<DeploymentDataPlaneLimits>): Readonly<DeploymentDataPlaneLimits>;
|
|
5
|
-
//# sourceMappingURL=data-plane-limits.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-plane-limits.d.ts","sourceRoot":"","sources":["../src/data-plane-limits.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,yBAAyB,EAC/B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,MAAM,yBAAyB,GAAG,yBAAyB,CAAC;AAElE,eAAO,MAAM,oCAAoC,qCAAuC,CAAC;AAEzF,wBAAgB,gCAAgC,CAC9C,KAAK,GAAE,OAAO,CAAC,yBAAyB,CAAM,GAC7C,QAAQ,CAAC,yBAAyB,CAAC,CAErC"}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { DEFAULT_PROTOCOL_SCHEMA_VALUE_LIMITS, resolveProtocolSchemaValueLimits, } from '@hypequery/protocol';
|
|
2
|
-
export const DEFAULT_DEPLOYMENT_DATA_PLANE_LIMITS = DEFAULT_PROTOCOL_SCHEMA_VALUE_LIMITS;
|
|
3
|
-
export function resolveDeploymentDataPlaneLimits(input = {}) {
|
|
4
|
-
return resolveProtocolSchemaValueLimits({ limits: input });
|
|
5
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ProtocolDeploymentReleaseTarget } from '@hypequery/protocol';
|
|
2
|
-
import type { DeploymentRuntimeReferenceExecutionInput } from './data-plane.js';
|
|
3
|
-
import type { DeploymentRuntimeSupervisor } from './runtime-supervisor.js';
|
|
4
|
-
export interface DeploymentRuntimeSupervisorExecutorOptions {
|
|
5
|
-
readonly supervisor: DeploymentRuntimeSupervisor;
|
|
6
|
-
readonly target: ProtocolDeploymentReleaseTarget;
|
|
7
|
-
/** The immutable activation generation used to build the matching data plane. */
|
|
8
|
-
readonly activationRevision: string;
|
|
9
|
-
/** Convert provider-neutral execution context into the runtime handler's argument contract. */
|
|
10
|
-
readonly argument: (input: DeploymentRuntimeReferenceExecutionInput) => unknown;
|
|
11
|
-
}
|
|
12
|
-
export declare function createDeploymentRuntimeSupervisorExecutor(options: DeploymentRuntimeSupervisorExecutorOptions): (input: DeploymentRuntimeReferenceExecutionInput) => Promise<unknown>;
|
|
13
|
-
//# sourceMappingURL=data-plane-runtime.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-plane-runtime.d.ts","sourceRoot":"","sources":["../src/data-plane-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,KAAK,EACV,wCAAwC,EACzC,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,WAAW,0CAA0C;IACzD,QAAQ,CAAC,UAAU,EAAE,2BAA2B,CAAC;IACjD,QAAQ,CAAC,MAAM,EAAE,+BAA+B,CAAC;IACjD,iFAAiF;IACjF,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,wCAAwC,KAAK,OAAO,CAAC;CACjF;AAED,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,0CAA0C,GAClD,CAAC,KAAK,EAAE,wCAAwC,KAAK,OAAO,CAAC,OAAO,CAAC,CAWvE"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export function createDeploymentRuntimeSupervisorExecutor(options) {
|
|
2
|
-
if (!/^[0-9a-f]{64}$/.test(options.activationRevision)) {
|
|
3
|
-
throw new RangeError('activationRevision must be a lowercase SHA-256 identity');
|
|
4
|
-
}
|
|
5
|
-
return async (input) => await options.supervisor.invoke({
|
|
6
|
-
target: options.target,
|
|
7
|
-
activationRevision: options.activationRevision,
|
|
8
|
-
query: input.query.name,
|
|
9
|
-
argument: options.argument(input),
|
|
10
|
-
signal: input.signal,
|
|
11
|
-
});
|
|
12
|
-
}
|