@jskit-ai/http-runtime 0.1.243 → 0.1.244

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.
Files changed (35) hide show
  1. package/package.json +6 -35
  2. package/src/client/index.js +2 -3
  3. package/src/shared/clientRuntime/client.js +1 -57
  4. package/src/shared/clientRuntime/errors.js +39 -2
  5. package/src/shared/clientRuntime/jsonApiResourceTransport.js +27 -39
  6. package/src/shared/index.js +2 -21
  7. package/src/shared/validators/errorResponses.js +1 -13
  8. package/src/shared/validators/jsonApiQueryTransport.js +1 -5
  9. package/src/shared/validators/jsonApiRouteTransport.js +43 -54
  10. package/src/shared/validators/jsonApiTransport.js +1 -5
  11. package/src/shared/validators/operationValidation.js +5 -92
  12. package/test/client.test.js +29 -0
  13. package/test/entrypoints.boundary.test.js +15 -14
  14. package/test/errorResponses.test.js +0 -11
  15. package/test/jsonApiRouteTransport.test.js +12 -7
  16. package/test/operationValidation.test.js +13 -61
  17. package/test/transientRetryHttpClient.test.js +11 -0
  18. package/test/validationErrors.test.js +20 -9
  19. package/src/client/providers/HttpClientRuntimeClientProvider.js +0 -20
  20. package/src/client/providers/HttpValidatorsClientProvider.js +0 -16
  21. package/src/server/providers/HttpClientProvider.js +0 -16
  22. package/src/server/providers/HttpValidatorsProvider.js +0 -16
  23. package/src/shared/clientRuntime/index.js +0 -4
  24. package/src/shared/validators/command.js +0 -24
  25. package/src/shared/validators/httpValidatorsApi.js +0 -129
  26. package/src/shared/validators/jsonApiResponses.js +0 -3
  27. package/src/shared/validators/operationMessages.js +0 -318
  28. package/src/shared/validators/paginationQuery.js +0 -27
  29. package/src/shared/validators/resource.js +0 -41
  30. package/test/command.test.js +0 -66
  31. package/test/operationMessages.test.js +0 -197
  32. package/test/paginationQuery.test.js +0 -28
  33. package/test/providerRuntime.httpClient.test.js +0 -22
  34. package/test/providerRuntime.validators.test.js +0 -22
  35. package/test/resource.test.js +0 -128
@@ -1,318 +0,0 @@
1
- import { isRecord, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
2
-
3
- function resolveIssueField(issue = {}) {
4
- const instancePath = normalizeText(issue.instancePath);
5
- if (instancePath) {
6
- const segments = instancePath
7
- .replace(/^\//, "")
8
- .split("/")
9
- .map((entry) => normalizeText(entry))
10
- .filter(Boolean);
11
-
12
- if (segments.length > 0) {
13
- return segments[0];
14
- }
15
- }
16
-
17
- const params = isRecord(issue.params) ? issue.params : {};
18
- const missingProperty = normalizeText(params.missingProperty);
19
- if (missingProperty) {
20
- return missingProperty;
21
- }
22
-
23
- const requiredProperties = Array.isArray(params.requiredProperties)
24
- ? params.requiredProperties
25
- : [];
26
- if (requiredProperties.length > 0) {
27
- return normalizeText(requiredProperties[0]);
28
- }
29
-
30
- const additionalProperties = Array.isArray(params.additionalProperties)
31
- ? params.additionalProperties
32
- : [];
33
- if (additionalProperties.length > 0) {
34
- return normalizeText(additionalProperties[0]);
35
- }
36
-
37
- const additionalProperty = normalizeText(params.additionalProperty);
38
- if (additionalProperty) {
39
- return additionalProperty;
40
- }
41
-
42
- return "";
43
- }
44
-
45
- function decodePointerSegment(value = "") {
46
- return String(value).replace(/~1/g, "/").replace(/~0/g, "~");
47
- }
48
-
49
- function resolveSchemaPathSegments(schemaPath = "") {
50
- const normalizedSchemaPath = normalizeText(schemaPath);
51
- if (!normalizedSchemaPath.startsWith("#")) {
52
- return [];
53
- }
54
-
55
- const pointer = normalizedSchemaPath.slice(1);
56
- if (!pointer) {
57
- return [];
58
- }
59
-
60
- return pointer
61
- .replace(/^\//, "")
62
- .split("/")
63
- .map((segment) => decodePointerSegment(segment))
64
- .filter((segment) => segment.length > 0);
65
- }
66
-
67
- function resolveFieldFromSchemaPath(schemaPath = "") {
68
- const segments = resolveSchemaPathSegments(schemaPath);
69
- for (let index = 0; index < segments.length - 1; index += 1) {
70
- if (segments[index] !== "properties") {
71
- continue;
72
- }
73
-
74
- const field = normalizeText(segments[index + 1]);
75
- if (field) {
76
- return field;
77
- }
78
- }
79
-
80
- return "";
81
- }
82
-
83
- function resolveSchemaNode(schema = {}, schemaPath = "") {
84
- const source = isRecord(schema) ? schema : {};
85
- const segments = resolveSchemaPathSegments(schemaPath);
86
- let current = source;
87
-
88
- for (const segment of segments) {
89
- if (Array.isArray(current)) {
90
- const index = Number.parseInt(segment, 10);
91
- if (!Number.isInteger(index) || index < 0 || index >= current.length) {
92
- return null;
93
- }
94
- current = current[index];
95
- continue;
96
- }
97
-
98
- if (!isRecord(current) || !Object.hasOwn(current, segment)) {
99
- return null;
100
- }
101
- current = current[segment];
102
- }
103
-
104
- return current;
105
- }
106
-
107
- function collectSchemaFieldCandidates(schema = {}) {
108
- if (!isRecord(schema)) {
109
- return [];
110
- }
111
-
112
- const fields = [];
113
- const seenFields = new Set();
114
- const addField = (value) => {
115
- const normalized = normalizeText(value);
116
- if (!normalized || seenFields.has(normalized)) {
117
- return;
118
- }
119
-
120
- seenFields.add(normalized);
121
- fields.push(normalized);
122
- };
123
-
124
- const properties = isRecord(schema.properties) ? schema.properties : {};
125
- for (const field of Object.keys(properties)) {
126
- addField(field);
127
- }
128
-
129
- const required = Array.isArray(schema.required) ? schema.required : [];
130
- for (const field of required) {
131
- addField(field);
132
- }
133
-
134
- const addFromSchema = (entry) => {
135
- if (!isRecord(entry)) {
136
- return;
137
- }
138
-
139
- const entryProperties = isRecord(entry.properties) ? entry.properties : {};
140
- for (const field of Object.keys(entryProperties)) {
141
- addField(field);
142
- }
143
-
144
- const entryRequired = Array.isArray(entry.required) ? entry.required : [];
145
- for (const field of entryRequired) {
146
- addField(field);
147
- }
148
- };
149
-
150
- for (const keyword of ["anyOf", "oneOf", "allOf"]) {
151
- const branches = Array.isArray(schema[keyword]) ? schema[keyword] : [];
152
- for (const branch of branches) {
153
- addFromSchema(branch);
154
- }
155
- }
156
-
157
- for (const keyword of ["if", "then", "else"]) {
158
- addFromSchema(schema[keyword]);
159
- }
160
-
161
- return fields;
162
- }
163
-
164
- function resolveConditionalIssueField(issue = {}, schema = {}) {
165
- const params = isRecord(issue.params) ? issue.params : {};
166
- const failingKeyword = normalizeText(params.failingKeyword).toLowerCase();
167
- if (failingKeyword !== "then" && failingKeyword !== "else") {
168
- return "";
169
- }
170
-
171
- const scopeSchema = resolveSchemaNode(schema, issue.schemaPath) || schema;
172
- if (!isRecord(scopeSchema)) {
173
- return "";
174
- }
175
-
176
- const conditionalSchema = scopeSchema[failingKeyword];
177
- const candidates = collectSchemaFieldCandidates(conditionalSchema);
178
- return candidates[0] || "";
179
- }
180
-
181
- function resolveFallbackIssueField(issue = {}, schema = {}) {
182
- const fromSchemaPath = resolveFieldFromSchemaPath(issue.schemaPath);
183
- if (fromSchemaPath) {
184
- return fromSchemaPath;
185
- }
186
-
187
- const keyword = normalizeText(issue.keyword).toLowerCase();
188
- if (keyword === "if") {
189
- return resolveConditionalIssueField(issue, schema);
190
- }
191
-
192
- return "";
193
- }
194
-
195
- function shouldSuppressRootUnionGlobalIssue(issue = {}, fieldErrors = {}) {
196
- if (Object.keys(fieldErrors).length < 1) {
197
- return false;
198
- }
199
-
200
- const keyword = normalizeText(issue.keyword).toLowerCase();
201
- if (keyword !== "anyof" && keyword !== "oneof" && keyword !== "allof") {
202
- return false;
203
- }
204
-
205
- const instancePath = normalizeText(issue.instancePath);
206
- const schemaPath = normalizeText(issue.schemaPath);
207
- return !instancePath && (schemaPath === "#" || schemaPath === "#/");
208
- }
209
-
210
- function resolveMissingRequiredFields(issue = {}) {
211
- const params = isRecord(issue.params) ? issue.params : {};
212
- const requiredProperties = Array.isArray(params.requiredProperties)
213
- ? params.requiredProperties
214
- : [];
215
-
216
- return requiredProperties
217
- .map((entry) => normalizeText(entry))
218
- .filter(Boolean);
219
- }
220
-
221
- function resolveSchemaMessages(schema = {}) {
222
- const source = isRecord(schema) ? schema : {};
223
- const messages = source.messages;
224
- return isRecord(messages) ? messages : {};
225
- }
226
-
227
- function resolveFieldSchema(schema = {}, field = "") {
228
- const source = isRecord(schema) ? schema : {};
229
- const properties = isRecord(source.properties) ? source.properties : {};
230
- const fieldSchema = properties[field];
231
- return isRecord(fieldSchema) ? fieldSchema : null;
232
- }
233
-
234
- function resolveIssueMessageFromSchema(field, issue, schema = {}) {
235
- const keyword = normalizeText(issue?.keyword);
236
-
237
- if (field) {
238
- const fieldSchema = resolveFieldSchema(schema, field);
239
- const fieldMessages = resolveSchemaMessages(fieldSchema);
240
-
241
- const fieldKeywordMessage = normalizeText(fieldMessages[keyword]);
242
- if (fieldKeywordMessage) {
243
- return fieldKeywordMessage;
244
- }
245
-
246
- const fieldDefaultMessage = normalizeText(fieldMessages.default);
247
- if (fieldDefaultMessage) {
248
- return fieldDefaultMessage;
249
- }
250
- }
251
-
252
- const schemaMessages = resolveSchemaMessages(schema);
253
- const schemaKeywordMessage = normalizeText(schemaMessages[keyword]);
254
- if (schemaKeywordMessage) {
255
- return schemaKeywordMessage;
256
- }
257
-
258
- const schemaDefaultMessage = normalizeText(schemaMessages.default);
259
- if (schemaDefaultMessage) {
260
- return schemaDefaultMessage;
261
- }
262
-
263
- const issueMessage = normalizeText(issue?.message);
264
- if (issueMessage) {
265
- return issueMessage;
266
- }
267
-
268
- return "Invalid value.";
269
- }
270
-
271
- function mapOperationIssues(issues = [], schema = {}) {
272
- const source = Array.isArray(issues) ? issues : [];
273
- const fieldErrors = {};
274
- const globalErrors = [];
275
-
276
- for (const issue of source) {
277
- const missingRequiredFields = resolveMissingRequiredFields(issue);
278
- if (missingRequiredFields.length > 0) {
279
- for (const field of missingRequiredFields) {
280
- if (Object.hasOwn(fieldErrors, field)) {
281
- continue;
282
- }
283
-
284
- fieldErrors[field] = resolveIssueMessageFromSchema(field, issue, schema);
285
- }
286
-
287
- continue;
288
- }
289
-
290
- const field = resolveIssueField(issue) || resolveFallbackIssueField(issue, schema);
291
- if (field) {
292
- if (!Object.hasOwn(fieldErrors, field)) {
293
- fieldErrors[field] = resolveIssueMessageFromSchema(field, issue, schema);
294
- }
295
- continue;
296
- }
297
-
298
- if (shouldSuppressRootUnionGlobalIssue(issue, fieldErrors)) {
299
- continue;
300
- }
301
-
302
- globalErrors.push(resolveIssueMessageFromSchema("", issue, schema));
303
- }
304
-
305
- return {
306
- fieldErrors,
307
- globalErrors
308
- };
309
- }
310
-
311
- export {
312
- resolveSchemaMessages,
313
- resolveFieldSchema,
314
- resolveIssueField,
315
- resolveMissingRequiredFields,
316
- resolveIssueMessageFromSchema,
317
- mapOperationIssues
318
- };
@@ -1,27 +0,0 @@
1
- import { createSchema } from "json-rest-schema";
2
-
3
- function createPaginationQuerySchema({
4
- defaultPage = 1,
5
- defaultPageSize = 10,
6
- minPage = 1,
7
- minPageSize = 1,
8
- maxPageSize = 100
9
- } = {}) {
10
- return createSchema({
11
- page: {
12
- type: "integer",
13
- required: false,
14
- min: minPage,
15
- default: defaultPage
16
- },
17
- pageSize: {
18
- type: "integer",
19
- required: false,
20
- min: minPageSize,
21
- max: maxPageSize,
22
- default: defaultPageSize
23
- }
24
- });
25
- }
26
-
27
- export { createPaginationQuerySchema };
@@ -1,41 +0,0 @@
1
- import { asSchemaDefinition } from "./schemaUtils.js";
2
- import { createCursorListValidator } from "@jskit-ai/kernel/shared/validators";
3
- import { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
4
-
5
- function createCursorPagedListResponseSchema(itemSchema) {
6
- return createCursorListValidator(itemSchema);
7
- }
8
-
9
- function createResource({
10
- record,
11
- create,
12
- replace,
13
- patch,
14
- list = null,
15
- listItem = null
16
- } = {}) {
17
- const normalizedRecordSchema = asSchemaDefinition(record, "record", "replace");
18
- const normalizedCreateSchema = asSchemaDefinition(create, "create", "create");
19
- const normalizedReplaceSchema = asSchemaDefinition(replace, "replace", "replace");
20
- const normalizedPatchSchema = asSchemaDefinition(patch, "patch", "patch");
21
- const normalizedListItemSchema = listItem
22
- ? asSchemaDefinition(listItem, "listItem", "replace")
23
- : normalizedRecordSchema;
24
- const normalizedListSchema = list
25
- ? asSchemaDefinition(list, "list", "replace")
26
- : createCursorPagedListResponseSchema(normalizedListItemSchema);
27
-
28
- return deepFreeze({
29
- record: normalizedRecordSchema,
30
- create: normalizedCreateSchema,
31
- replace: normalizedReplaceSchema,
32
- patch: normalizedPatchSchema,
33
- listItem: normalizedListItemSchema,
34
- list: normalizedListSchema
35
- });
36
- }
37
-
38
- export {
39
- createCursorPagedListResponseSchema,
40
- createResource
41
- };
@@ -1,66 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { createSchema } from "json-rest-schema";
4
- import { createCommand } from "../src/shared/validators/command.js";
5
-
6
- test("createCommand requires input/output schema definitions", () => {
7
- assert.throws(() => createCommand({}), /input is required/);
8
-
9
- assert.throws(
10
- () =>
11
- createCommand({
12
- input: {
13
- schema: createSchema({})
14
- }
15
- }),
16
- /output is required/
17
- );
18
-
19
- assert.throws(
20
- () =>
21
- createCommand({
22
- input: createSchema({}),
23
- output: {
24
- schema: createSchema({})
25
- }
26
- }),
27
- /input must be a schema definition object/
28
- );
29
- });
30
-
31
- test("createCommand normalizes invalidates and preserves idempotent flag", () => {
32
- const command = createCommand({
33
- input: {
34
- schema: createSchema({
35
- token: { type: "string", required: true, minLength: 1 }
36
- })
37
- },
38
- output: {
39
- schema: createSchema({
40
- ok: { type: "boolean", required: true }
41
- })
42
- },
43
- idempotent: true,
44
- invalidates: ["users-web", "users-web", "", "workspace.members"]
45
- });
46
-
47
- assert.equal(command.idempotent, true);
48
- assert.deepEqual(command.invalidates, ["users-web", "workspace.members"]);
49
- assert.equal(command.input.mode, "patch");
50
- assert.equal(command.output.mode, "replace");
51
- assert.equal(typeof command.input.schema.toJsonSchema, "function");
52
- assert.equal(typeof command.output.schema.toJsonSchema, "function");
53
- });
54
-
55
- test("createCommand omits idempotent when not explicitly boolean", () => {
56
- const command = createCommand({
57
- input: {
58
- schema: createSchema({})
59
- },
60
- output: {
61
- schema: createSchema({})
62
- }
63
- });
64
-
65
- assert.equal(Object.hasOwn(command, "idempotent"), false);
66
- });
@@ -1,197 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import {
4
- mapOperationIssues,
5
- resolveFieldSchema,
6
- resolveIssueField,
7
- resolveMissingRequiredFields,
8
- resolveSchemaMessages
9
- } from "../src/shared/validators/operationMessages.js";
10
-
11
- const sampleSchema = {
12
- type: "object",
13
- properties: {
14
- name: {
15
- type: "string",
16
- minLength: 1,
17
- messages: {
18
- required: "Workspace name is required.",
19
- minLength: "Workspace name is required.",
20
- default: "Invalid workspace name."
21
- }
22
- },
23
- color: {
24
- type: "string",
25
- pattern: "^#[0-9A-Fa-f]{6}$",
26
- messages: {
27
- pattern: "Workspace color must be a hex value."
28
- }
29
- },
30
- invitesEnabled: {
31
- type: "boolean",
32
- messages: {
33
- default: "invitesEnabled must be true or false."
34
- }
35
- }
36
- },
37
- additionalProperties: false,
38
- messages: {
39
- additionalProperties: "Unexpected field."
40
- }
41
- };
42
-
43
- test("resolveIssueField resolves missing and nested fields", () => {
44
- const requiredIssue = {
45
- keyword: "required",
46
- params: {
47
- missingProperty: "name",
48
- requiredProperties: ["name"]
49
- }
50
- };
51
-
52
- assert.equal(resolveIssueField(requiredIssue), "name");
53
- assert.deepEqual(resolveMissingRequiredFields(requiredIssue), ["name"]);
54
-
55
- const minLengthIssue = {
56
- keyword: "minLength",
57
- instancePath: "/profile/displayName"
58
- };
59
- assert.equal(resolveIssueField(minLengthIssue), "profile");
60
- });
61
-
62
- test("mapOperationIssues applies field message overrides by keyword", () => {
63
- const issues = [
64
- {
65
- keyword: "minLength",
66
- instancePath: "/name"
67
- },
68
- {
69
- keyword: "pattern",
70
- instancePath: "/color"
71
- },
72
- {
73
- keyword: "type",
74
- instancePath: "/invitesEnabled"
75
- }
76
- ];
77
- const mapped = mapOperationIssues(issues, sampleSchema);
78
-
79
- assert.equal(mapped.fieldErrors.name, "Workspace name is required.");
80
- assert.equal(mapped.fieldErrors.color, "Workspace color must be a hex value.");
81
- assert.equal(mapped.fieldErrors.invitesEnabled, "invitesEnabled must be true or false.");
82
- assert.deepEqual(mapped.globalErrors, []);
83
- });
84
-
85
- test("mapOperationIssues falls back to keyword/global messages", () => {
86
- const issues = [
87
- {
88
- keyword: "additionalProperties",
89
- params: {
90
- additionalProperty: "extra"
91
- }
92
- }
93
- ];
94
- const mapped = mapOperationIssues(issues, sampleSchema);
95
-
96
- assert.equal(mapped.fieldErrors.extra, "Unexpected field.");
97
- });
98
-
99
- test("mapOperationIssues maps conditional schema failures to field errors", () => {
100
- const conditionalSchema = {
101
- type: "object",
102
- properties: {
103
- isVaccinated: {
104
- type: "boolean"
105
- },
106
- adenovirusValidTo: {
107
- type: "string"
108
- }
109
- },
110
- if: {
111
- properties: {
112
- isVaccinated: {
113
- const: true
114
- }
115
- }
116
- },
117
- then: {
118
- required: ["adenovirusValidTo"]
119
- },
120
- messages: {
121
- if: "Adenovirus valid-to date is required when vaccinated."
122
- }
123
- };
124
-
125
- const issues = [
126
- {
127
- keyword: "if",
128
- schemaPath: "#",
129
- params: {
130
- failingKeyword: "then"
131
- }
132
- }
133
- ];
134
- const mapped = mapOperationIssues(issues, conditionalSchema);
135
-
136
- assert.equal(mapped.fieldErrors.adenovirusValidTo, "Adenovirus valid-to date is required when vaccinated.");
137
- assert.deepEqual(mapped.globalErrors, []);
138
- });
139
-
140
- test("mapOperationIssues suppresses redundant root anyOf global issue when field errors exist", () => {
141
- const unionSchema = {
142
- anyOf: [
143
- {
144
- type: "object",
145
- properties: {
146
- kind: {
147
- const: "dog"
148
- },
149
- bark: {
150
- type: "string",
151
- minLength: 1
152
- }
153
- },
154
- additionalProperties: false
155
- },
156
- {
157
- type: "object",
158
- properties: {
159
- kind: {
160
- const: "cat"
161
- },
162
- meow: {
163
- type: "string",
164
- minLength: 1
165
- }
166
- },
167
- additionalProperties: false
168
- }
169
- ]
170
- };
171
-
172
- const issues = [
173
- {
174
- keyword: "minLength",
175
- instancePath: "/bark"
176
- },
177
- {
178
- keyword: "anyOf",
179
- schemaPath: "#"
180
- }
181
- ];
182
- const mapped = mapOperationIssues(issues, unionSchema);
183
-
184
- assert.equal(typeof mapped.fieldErrors.bark, "string");
185
- assert.deepEqual(mapped.globalErrors, []);
186
- });
187
-
188
- test("schema message helpers resolve field and root messages", () => {
189
- const nameSchema = resolveFieldSchema(sampleSchema, "name");
190
- assert.equal(typeof nameSchema, "object");
191
-
192
- const nameMessages = resolveSchemaMessages(nameSchema);
193
- const rootMessages = resolveSchemaMessages(sampleSchema);
194
-
195
- assert.equal(nameMessages.minLength, "Workspace name is required.");
196
- assert.equal(rootMessages.additionalProperties, "Unexpected field.");
197
- });
@@ -1,28 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
-
4
- import { createPaginationQuerySchema } from "../src/shared/validators/paginationQuery.js";
5
-
6
- test("createPaginationQuerySchema exports the expected transport bounds", () => {
7
- const schema = createPaginationQuerySchema().toJsonSchema({ mode: "patch" });
8
-
9
- assert.equal(schema.type, "object");
10
- assert.equal(schema.additionalProperties, false);
11
- assert.equal(schema.properties.page.minimum, 1);
12
- assert.equal(schema.properties.pageSize.minimum, 1);
13
- assert.equal(schema.properties.pageSize.maximum, 100);
14
- });
15
-
16
- test("createPaginationQuerySchema applies custom transport bounds", () => {
17
- const schema = createPaginationQuerySchema({
18
- defaultPage: 2,
19
- defaultPageSize: 25,
20
- minPage: 2,
21
- minPageSize: 5,
22
- maxPageSize: 250
23
- }).toJsonSchema({ mode: "patch" });
24
-
25
- assert.equal(schema.properties.page.minimum, 2);
26
- assert.equal(schema.properties.pageSize.minimum, 5);
27
- assert.equal(schema.properties.pageSize.maximum, 250);
28
- });
@@ -1,22 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
-
4
- import { createCapabilityRuntime, defineProvider } from "@jskit-ai/kernel/shared/capabilities";
5
- import { HttpClientProvider } from "../src/server/providers/HttpClientProvider.js";
6
-
7
- test("HttpClientProvider provides the server HTTP client API", async () => {
8
- let httpClient;
9
- const consumer = defineProvider({
10
- id: "test.http-client.consumer",
11
- requires: { value: "runtime.http-client" },
12
- setup({ value }) {
13
- httpClient = value;
14
- return {};
15
- }
16
- });
17
- const runtime = createCapabilityRuntime({ providers: [HttpClientProvider, consumer] });
18
- await runtime.start();
19
- assert.equal(typeof httpClient.createHttpClient, "function");
20
- assert.equal(typeof httpClient.shouldRetryForCsrfFailure, "function");
21
- await runtime.shutdown();
22
- });