@habitat-ai/resource-rule-evaluation 0.2.4 → 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/dist/contract.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ import type { Effect } from "effect";
|
|
|
2
2
|
import { type Static, Type } from "typebox";
|
|
3
3
|
/** Maximum detail retained by one rule-evaluation failure. */
|
|
4
4
|
export declare const MAX_RULE_EVALUATION_FAILURE_DETAIL = 4096;
|
|
5
|
+
/** Structural schema for one caller-identified, already-resolved program. */
|
|
6
|
+
export declare const RuleEvaluationProgramSchema: Type.TObject<{
|
|
7
|
+
id: Type.TReadonly<Type.TString>;
|
|
8
|
+
program: Type.TReadonly<Type.TString>;
|
|
9
|
+
}>;
|
|
5
10
|
/** Structural schema for one source position reported by an evaluator. */
|
|
6
11
|
export declare const RuleEvaluationPositionSchema: Type.TObject<{
|
|
7
12
|
line: Type.TReadonly<Type.TInteger>;
|
|
@@ -23,13 +28,17 @@ export declare const RuleEvaluationFindingSchema: Type.TObject<{
|
|
|
23
28
|
}>>;
|
|
24
29
|
message: Type.TReadonly<Type.TUnion<[Type.TString, Type.TNull]>>;
|
|
25
30
|
}>;
|
|
26
|
-
/** Structural schema for one
|
|
31
|
+
/** Structural schema for one exact-subject evaluation batch. */
|
|
27
32
|
export declare const RuleEvaluationRequestSchema: Type.TObject<{
|
|
28
|
-
|
|
33
|
+
programs: Type.TReadonly<Type.TImmutable<Type.TArray<Type.TObject<{
|
|
34
|
+
id: Type.TReadonly<Type.TString>;
|
|
35
|
+
program: Type.TReadonly<Type.TString>;
|
|
36
|
+
}>>>>;
|
|
29
37
|
subjectPaths: Type.TReadonly<Type.TImmutable<Type.TArray<Type.TString>>>;
|
|
30
38
|
}>;
|
|
31
|
-
/** Structural schema for one
|
|
32
|
-
export declare const
|
|
39
|
+
/** Structural schema for one program's attributed evaluation result. */
|
|
40
|
+
export declare const RuleEvaluationProgramResultSchema: Type.TObject<{
|
|
41
|
+
programId: Type.TReadonly<Type.TString>;
|
|
33
42
|
findings: Type.TReadonly<Type.TImmutable<Type.TArray<Type.TObject<{
|
|
34
43
|
path: Type.TReadonly<Type.TString>;
|
|
35
44
|
start: Type.TReadonly<Type.TObject<{
|
|
@@ -45,6 +54,26 @@ export declare const RuleEvaluationResultSchema: Type.TObject<{
|
|
|
45
54
|
message: Type.TReadonly<Type.TUnion<[Type.TString, Type.TNull]>>;
|
|
46
55
|
}>>>>;
|
|
47
56
|
}>;
|
|
57
|
+
/** Structural schema for one completed exact-subject evaluation batch. */
|
|
58
|
+
export declare const RuleEvaluationResultSchema: Type.TObject<{
|
|
59
|
+
results: Type.TReadonly<Type.TImmutable<Type.TArray<Type.TObject<{
|
|
60
|
+
programId: Type.TReadonly<Type.TString>;
|
|
61
|
+
findings: Type.TReadonly<Type.TImmutable<Type.TArray<Type.TObject<{
|
|
62
|
+
path: Type.TReadonly<Type.TString>;
|
|
63
|
+
start: Type.TReadonly<Type.TObject<{
|
|
64
|
+
line: Type.TReadonly<Type.TInteger>;
|
|
65
|
+
column: Type.TReadonly<Type.TInteger>;
|
|
66
|
+
offset: Type.TReadonly<Type.TInteger>;
|
|
67
|
+
}>>;
|
|
68
|
+
end: Type.TReadonly<Type.TObject<{
|
|
69
|
+
line: Type.TReadonly<Type.TInteger>;
|
|
70
|
+
column: Type.TReadonly<Type.TInteger>;
|
|
71
|
+
offset: Type.TReadonly<Type.TInteger>;
|
|
72
|
+
}>>;
|
|
73
|
+
message: Type.TReadonly<Type.TUnion<[Type.TString, Type.TNull]>>;
|
|
74
|
+
}>>>>;
|
|
75
|
+
}>>>>;
|
|
76
|
+
}>;
|
|
48
77
|
/** Provider-neutral mechanical failure reasons for one evaluation attempt. */
|
|
49
78
|
export declare const RuleEvaluationFailureReasonSchema: Type.TUnion<[Type.TLiteral<"InvalidInput">, Type.TLiteral<"SetupFailed">, Type.TLiteral<"ExecutionFailed">, Type.TLiteral<"TimedOut">, Type.TLiteral<"InvalidOutput">]>;
|
|
50
79
|
/** Structural schema for one bounded typed evaluation failure. */
|
|
@@ -57,9 +86,13 @@ export declare const RuleEvaluationFailureSchema: Type.TObject<{
|
|
|
57
86
|
export type RuleEvaluationPosition = Static<typeof RuleEvaluationPositionSchema>;
|
|
58
87
|
/** One provider-neutral finding. */
|
|
59
88
|
export type RuleEvaluationFinding = Static<typeof RuleEvaluationFindingSchema>;
|
|
60
|
-
/** One already-resolved
|
|
89
|
+
/** One caller-identified, already-resolved evaluator program. */
|
|
90
|
+
export type RuleEvaluationProgram = Static<typeof RuleEvaluationProgramSchema>;
|
|
91
|
+
/** One exact-subject evaluation batch. */
|
|
61
92
|
export type RuleEvaluationRequest = Static<typeof RuleEvaluationRequestSchema>;
|
|
62
|
-
/** One completed evaluation
|
|
93
|
+
/** One completed program result within an evaluation batch. */
|
|
94
|
+
export type RuleEvaluationProgramResult = Static<typeof RuleEvaluationProgramResultSchema>;
|
|
95
|
+
/** One completed exact-subject evaluation batch. */
|
|
63
96
|
export type RuleEvaluationResult = Static<typeof RuleEvaluationResultSchema>;
|
|
64
97
|
/** One provider-neutral mechanical failure reason. */
|
|
65
98
|
export type RuleEvaluationFailureReason = Static<typeof RuleEvaluationFailureReasonSchema>;
|
|
@@ -67,7 +100,7 @@ export type RuleEvaluationFailureReason = Static<typeof RuleEvaluationFailureRea
|
|
|
67
100
|
export type RuleEvaluationFailure = Static<typeof RuleEvaluationFailureSchema>;
|
|
68
101
|
/** Checks an unknown value against the complete rule-evaluation failure contract. */
|
|
69
102
|
export declare function isRuleEvaluationFailure(input: unknown): input is RuleEvaluationFailure;
|
|
70
|
-
/** Provider-neutral capability for evaluating one
|
|
103
|
+
/** Provider-neutral capability for evaluating resolved programs over one exact subject set. */
|
|
71
104
|
export interface RuleEvaluationResource<R = never> {
|
|
72
105
|
readonly evaluate: (input: RuleEvaluationRequest) => Effect.Effect<RuleEvaluationResult, RuleEvaluationFailure, R>;
|
|
73
106
|
}
|
package/dist/contract.js
CHANGED
|
@@ -5,6 +5,18 @@ export const MAX_RULE_EVALUATION_FAILURE_DETAIL = 4_096;
|
|
|
5
5
|
const NonEmptyStringSchema = Type.String({
|
|
6
6
|
minLength: 1,
|
|
7
7
|
});
|
|
8
|
+
/** Structural schema for one caller-identified, already-resolved program. */
|
|
9
|
+
export const RuleEvaluationProgramSchema = ReadonlyObject(Type.Object({
|
|
10
|
+
id: Type.String({
|
|
11
|
+
minLength: 1,
|
|
12
|
+
maxLength: 1_024,
|
|
13
|
+
description: "Invocation-local program identity used to attribute findings",
|
|
14
|
+
}),
|
|
15
|
+
program: Type.String({
|
|
16
|
+
minLength: 1,
|
|
17
|
+
description: "Already-resolved evaluator program",
|
|
18
|
+
}),
|
|
19
|
+
}), { additionalProperties: false });
|
|
8
20
|
/** Structural schema for one source position reported by an evaluator. */
|
|
9
21
|
export const RuleEvaluationPositionSchema = ReadonlyObject(Type.Object({
|
|
10
22
|
line: Type.Integer({
|
|
@@ -32,23 +44,35 @@ export const RuleEvaluationFindingSchema = ReadonlyObject(Type.Object({
|
|
|
32
44
|
description: "Evaluator-authored diagnostic message when one was produced",
|
|
33
45
|
}),
|
|
34
46
|
}), { additionalProperties: false });
|
|
35
|
-
/** Structural schema for one
|
|
47
|
+
/** Structural schema for one exact-subject evaluation batch. */
|
|
36
48
|
export const RuleEvaluationRequestSchema = ReadonlyObject(Type.Object({
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
description: "
|
|
49
|
+
programs: ReadonlyObject(Type.Array(RuleEvaluationProgramSchema), {
|
|
50
|
+
minItems: 1,
|
|
51
|
+
description: "Non-empty ordered programs evaluated against the same subjects",
|
|
40
52
|
}),
|
|
41
53
|
subjectPaths: ReadonlyObject(Type.Array(NonEmptyStringSchema), {
|
|
42
54
|
minItems: 1,
|
|
43
55
|
description: "Non-empty caller-resolved absolute subject paths",
|
|
44
56
|
}),
|
|
45
57
|
}), { additionalProperties: false });
|
|
46
|
-
/** Structural schema for one
|
|
47
|
-
export const
|
|
58
|
+
/** Structural schema for one program's attributed evaluation result. */
|
|
59
|
+
export const RuleEvaluationProgramResultSchema = ReadonlyObject(Type.Object({
|
|
60
|
+
programId: Type.String({
|
|
61
|
+
minLength: 1,
|
|
62
|
+
maxLength: 1_024,
|
|
63
|
+
description: "Invocation-local identity of the evaluated program",
|
|
64
|
+
}),
|
|
48
65
|
findings: ReadonlyObject(Type.Array(RuleEvaluationFindingSchema), {
|
|
49
66
|
description: "Findings reported by the evaluator in provider-defined stable order",
|
|
50
67
|
}),
|
|
51
68
|
}), { additionalProperties: false });
|
|
69
|
+
/** Structural schema for one completed exact-subject evaluation batch. */
|
|
70
|
+
export const RuleEvaluationResultSchema = ReadonlyObject(Type.Object({
|
|
71
|
+
results: ReadonlyObject(Type.Array(RuleEvaluationProgramResultSchema), {
|
|
72
|
+
minItems: 1,
|
|
73
|
+
description: "Per-program results in request order",
|
|
74
|
+
}),
|
|
75
|
+
}), { additionalProperties: false });
|
|
52
76
|
/** Provider-neutral mechanical failure reasons for one evaluation attempt. */
|
|
53
77
|
export const RuleEvaluationFailureReasonSchema = Type.Union([
|
|
54
78
|
Type.Literal("InvalidInput"),
|
|
@@ -6,9 +6,10 @@ import { ReadonlyObject, Type } from "typebox";
|
|
|
6
6
|
import Schema from "typebox/schema";
|
|
7
7
|
import { Value } from "typebox/value";
|
|
8
8
|
const TEMP_CATALOG_PREFIX = "habitat-rule-evaluation-";
|
|
9
|
-
const
|
|
10
|
-
/**
|
|
11
|
-
const
|
|
9
|
+
const GRIT_PATTERN_PREFIX = "habitat_rule_evaluation_";
|
|
10
|
+
/** Output retained per program, capped so an untrusted batch cannot allocate without bound. */
|
|
11
|
+
const MAX_GRIT_OUTPUT_BYTES_PER_PROGRAM = 256 * 1_024;
|
|
12
|
+
const MAX_GRIT_BATCH_OUTPUT_BYTES = 16 * 1_024 * 1_024;
|
|
12
13
|
/** Structural schema for Grit provider construction. */
|
|
13
14
|
export const GritRuleEvaluationProviderConfigSchema = ReadonlyObject(Type.Object({
|
|
14
15
|
executable: Type.String({
|
|
@@ -27,11 +28,12 @@ const GritPositionSchema = Type.Object({
|
|
|
27
28
|
}, { additionalProperties: false, description: "Grit-reported source position" });
|
|
28
29
|
const GritResultSchema = Type.Object({
|
|
29
30
|
check_id: Type.String({
|
|
30
|
-
|
|
31
|
-
description: "
|
|
31
|
+
minLength: 1,
|
|
32
|
+
description: "Grit check identity with evaluator suffix",
|
|
32
33
|
}),
|
|
33
|
-
local_name: Type.
|
|
34
|
-
|
|
34
|
+
local_name: Type.String({
|
|
35
|
+
minLength: 1,
|
|
36
|
+
description: "Local Grit catalog pattern name",
|
|
35
37
|
}),
|
|
36
38
|
path: Type.String({ minLength: 1, description: "Grit-reported subject path" }),
|
|
37
39
|
start: GritPositionSchema,
|
|
@@ -65,13 +67,24 @@ export function makeGritRuleEvaluationResource(config) {
|
|
|
65
67
|
return yield* fail("InvalidInput", "Grit provider configuration does not match its structural contract");
|
|
66
68
|
}
|
|
67
69
|
if (!requestValidator.Check(input)) {
|
|
68
|
-
return yield* fail("InvalidInput", "Rule-evaluation request requires
|
|
70
|
+
return yield* fail("InvalidInput", "Rule-evaluation request requires resolved programs and non-empty subject paths");
|
|
69
71
|
}
|
|
70
72
|
const path = yield* Path.Path;
|
|
71
73
|
if (input.subjectPaths.some((subjectPath) => !path.isAbsolute(subjectPath))) {
|
|
72
74
|
return yield* fail("InvalidInput", "Rule-evaluation subject paths must be caller-resolved absolute paths");
|
|
73
75
|
}
|
|
74
|
-
|
|
76
|
+
const [firstProgram, ...remainingPrograms] = input.programs;
|
|
77
|
+
if (firstProgram === undefined) {
|
|
78
|
+
return yield* fail("InvalidInput", "Rule-evaluation request requires at least one program");
|
|
79
|
+
}
|
|
80
|
+
if (new Set(input.programs.map(({ id }) => id)).size !== input.programs.length) {
|
|
81
|
+
return yield* fail("InvalidInput", "Rule-evaluation program identities must be unique");
|
|
82
|
+
}
|
|
83
|
+
const programs = [
|
|
84
|
+
materializeProgram(firstProgram, 0),
|
|
85
|
+
...remainingPrograms.map((program, index) => materializeProgram(program, index + 1)),
|
|
86
|
+
];
|
|
87
|
+
return yield* Effect.scoped(withScopedGritCatalog(programs, (catalog) => runGritCheck(config, catalog, programs, input.subjectPaths)));
|
|
75
88
|
});
|
|
76
89
|
return Object.freeze({ evaluate });
|
|
77
90
|
}
|
|
@@ -82,7 +95,7 @@ export function makeNodeGritRuleEvaluationResource(config) {
|
|
|
82
95
|
evaluate: (input) => resource.evaluate(input).pipe(Effect.provide(NodeServices.layer)),
|
|
83
96
|
});
|
|
84
97
|
}
|
|
85
|
-
function withScopedGritCatalog(
|
|
98
|
+
function withScopedGritCatalog(programs, use) {
|
|
86
99
|
return Effect.gen(function* () {
|
|
87
100
|
const fs = yield* FileSystem.FileSystem;
|
|
88
101
|
const path = yield* Path.Path;
|
|
@@ -105,12 +118,13 @@ function withScopedGritCatalog(program, use) {
|
|
|
105
118
|
.makeDirectory(catalog.cacheDirectory)
|
|
106
119
|
.pipe(mapPlatform("SetupFailed", "Failed to create Grit cache directory"));
|
|
107
120
|
yield* fs
|
|
108
|
-
.writeFileString(path.join(catalog.gritDirectory, "grit.yaml"), renderGritCatalog(
|
|
121
|
+
.writeFileString(path.join(catalog.gritDirectory, "grit.yaml"), renderGritCatalog(programs))
|
|
109
122
|
.pipe(mapPlatform("SetupFailed", "Failed to write temporary Grit catalog"));
|
|
110
123
|
return yield* use(catalog);
|
|
111
124
|
});
|
|
112
125
|
}
|
|
113
|
-
function runGritCheck(config, catalog, subjectPaths) {
|
|
126
|
+
function runGritCheck(config, catalog, programs, subjectPaths) {
|
|
127
|
+
const outputLimit = gritOutputLimit(programs.length);
|
|
114
128
|
const observe = Effect.gen(function* () {
|
|
115
129
|
const command = ChildProcess.make(config.executable, ["--json", "check", "--no-cache", "--grit-dir", catalog.gritDirectory, ...subjectPaths], {
|
|
116
130
|
cwd: catalog.root,
|
|
@@ -120,6 +134,7 @@ function runGritCheck(config, catalog, subjectPaths) {
|
|
|
120
134
|
GRIT_USER_CONFIG: catalog.userConfigDirectory,
|
|
121
135
|
GRIT_TELEMETRY_DISABLED: "true",
|
|
122
136
|
NO_COLOR: undefined,
|
|
137
|
+
RAYON_NUM_THREADS: "2",
|
|
123
138
|
},
|
|
124
139
|
extendEnv: true,
|
|
125
140
|
stdin: "ignore",
|
|
@@ -128,8 +143,8 @@ function runGritCheck(config, catalog, subjectPaths) {
|
|
|
128
143
|
});
|
|
129
144
|
const process = yield* command.pipe(mapPlatform("ExecutionFailed", "Failed to start Grit"));
|
|
130
145
|
const output = yield* Effect.all({
|
|
131
|
-
stdout: collectBoundedOutput(process.stdout, "stdout"),
|
|
132
|
-
stderr: collectBoundedOutput(process.stderr, "stderr"),
|
|
146
|
+
stdout: collectBoundedOutput(process.stdout, "stdout", outputLimit),
|
|
147
|
+
stderr: collectBoundedOutput(process.stderr, "stderr", outputLimit),
|
|
133
148
|
exitCode: process.exitCode.pipe(Effect.map(Number), mapPlatform("ExecutionFailed", "Failed to observe Grit exit")),
|
|
134
149
|
}, { concurrency: "unbounded" });
|
|
135
150
|
return Object.freeze(output);
|
|
@@ -147,7 +162,7 @@ function runGritCheck(config, catalog, subjectPaths) {
|
|
|
147
162
|
if (observation.stdout.trim().length > 0) {
|
|
148
163
|
return fail("InvalidOutput", "Grit check emitted unexpected stdout alongside its stderr JSON report");
|
|
149
164
|
}
|
|
150
|
-
return decodeGritReport(observation.stderr).pipe(Effect.
|
|
165
|
+
return decodeGritReport(observation.stderr).pipe(Effect.flatMap((report) => resultFromGritReport(report, programs)));
|
|
151
166
|
}));
|
|
152
167
|
}
|
|
153
168
|
function decodeGritReport(stderr) {
|
|
@@ -159,47 +174,61 @@ function decodeGritReport(stderr) {
|
|
|
159
174
|
catch: (error) => failure("InvalidOutput", `Grit check emitted an invalid report: ${errorMessage(error)}`),
|
|
160
175
|
})));
|
|
161
176
|
}
|
|
162
|
-
function resultFromGritReport(report) {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
function resultFromGritReport(report, programs) {
|
|
178
|
+
const programsByPattern = new Map(programs.map((program) => [program.patternName, program]));
|
|
179
|
+
const findingsByProgram = new Map(programs.map(({ id }) => [id, []]));
|
|
180
|
+
for (const result of report.results) {
|
|
181
|
+
const program = programsByPattern.get(result.local_name);
|
|
182
|
+
if (program === undefined || !result.check_id.startsWith(`#${result.local_name}/`)) {
|
|
183
|
+
return fail("InvalidOutput", `Grit check reported unexpected pattern identity '${result.local_name}'`);
|
|
184
|
+
}
|
|
185
|
+
findingsByProgram.get(program.id)?.push(Object.freeze({
|
|
186
|
+
path: result.path,
|
|
187
|
+
start: Object.freeze({
|
|
188
|
+
line: result.start.line,
|
|
189
|
+
column: result.start.col,
|
|
190
|
+
offset: result.start.offset,
|
|
191
|
+
}),
|
|
192
|
+
end: Object.freeze({
|
|
193
|
+
line: result.end.line,
|
|
194
|
+
column: result.end.col,
|
|
195
|
+
offset: result.end.offset,
|
|
196
|
+
}),
|
|
197
|
+
message: result.extra.message,
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
return Effect.succeed(Object.freeze({
|
|
201
|
+
results: Object.freeze(programs.map(({ id }) => Object.freeze({
|
|
202
|
+
programId: id,
|
|
203
|
+
findings: Object.freeze((findingsByProgram.get(id) ?? []).sort(compareFindings)),
|
|
204
|
+
}))),
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
function materializeProgram(program, index) {
|
|
179
208
|
return Object.freeze({
|
|
180
|
-
|
|
209
|
+
id: program.id,
|
|
210
|
+
patternName: `${GRIT_PATTERN_PREFIX}${index}`,
|
|
211
|
+
program: program.program,
|
|
181
212
|
});
|
|
182
213
|
}
|
|
183
|
-
function renderGritCatalog(
|
|
214
|
+
function renderGritCatalog(programs) {
|
|
184
215
|
return `${JSON.stringify({
|
|
185
216
|
version: "0.0.2",
|
|
186
|
-
patterns:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
},
|
|
193
|
-
],
|
|
217
|
+
patterns: programs.map(({ id, patternName, program }) => Object.freeze({
|
|
218
|
+
name: patternName,
|
|
219
|
+
title: id,
|
|
220
|
+
level: "error",
|
|
221
|
+
body: program,
|
|
222
|
+
})),
|
|
194
223
|
}, undefined, 2)}\n`;
|
|
195
224
|
}
|
|
196
|
-
function collectBoundedOutput(stream, channel) {
|
|
197
|
-
return Stream.runFoldEffect(stream.pipe(Stream.mapError((error) => failure("ExecutionFailed", `Failed to drain Grit ${channel}: ${error.message}`))), () => Object.freeze({ buffer: new Uint8Array(
|
|
225
|
+
function collectBoundedOutput(stream, channel, outputLimit) {
|
|
226
|
+
return Stream.runFoldEffect(stream.pipe(Stream.mapError((error) => failure("ExecutionFailed", `Failed to drain Grit ${channel}: ${error.message}`))), () => Object.freeze({ buffer: new Uint8Array(outputLimit), bytes: 0 }), (state, chunk) => {
|
|
198
227
|
if (chunk.byteLength === 0)
|
|
199
228
|
return Effect.succeed(state);
|
|
200
229
|
const bytes = state.bytes + chunk.byteLength;
|
|
201
|
-
return bytes >
|
|
202
|
-
? fail("InvalidOutput", `Grit ${channel} exceeded its ${
|
|
230
|
+
return bytes > outputLimit
|
|
231
|
+
? fail("InvalidOutput", `Grit ${channel} exceeded its ${outputLimit}-byte output limit`)
|
|
203
232
|
: Effect.sync(() => {
|
|
204
233
|
state.buffer.set(chunk, state.bytes);
|
|
205
234
|
return Object.freeze({ buffer: state.buffer, bytes });
|
|
@@ -209,6 +238,9 @@ function collectBoundedOutput(stream, channel) {
|
|
|
209
238
|
catch: (error) => failure("InvalidOutput", `Grit ${channel} was not valid UTF-8: ${errorMessage(error)}`),
|
|
210
239
|
})));
|
|
211
240
|
}
|
|
241
|
+
function gritOutputLimit(programCount) {
|
|
242
|
+
return Math.min(MAX_GRIT_BATCH_OUTPUT_BYTES, MAX_GRIT_OUTPUT_BYTES_PER_PROGRAM * programCount);
|
|
243
|
+
}
|
|
212
244
|
function mapPlatform(reason, context) {
|
|
213
245
|
return (effect) => effect.pipe(Effect.mapError((error) => failure(reason, `${context}: ${error.message}`)));
|
|
214
246
|
}
|