@apifuse/provider-sdk 2.0.0-beta.1 → 2.1.0-beta.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.
Files changed (79) hide show
  1. package/AUTHORING.md +93 -0
  2. package/CHANGELOG.md +21 -0
  3. package/README.md +133 -28
  4. package/bin/apifuse-check.ts +78 -71
  5. package/bin/apifuse-create.ts +12 -0
  6. package/bin/apifuse-dev.ts +24 -61
  7. package/bin/apifuse-pack-check.ts +87 -0
  8. package/bin/apifuse-pack-smoke.ts +122 -0
  9. package/bin/apifuse-perf.ts +33 -32
  10. package/bin/apifuse-record.ts +17 -7
  11. package/bin/apifuse-test.ts +6 -4
  12. package/bin/apifuse.ts +36 -35
  13. package/package.json +29 -9
  14. package/src/ceremonies/index.ts +768 -0
  15. package/src/cli/commands.ts +87 -0
  16. package/src/cli/create.ts +845 -0
  17. package/src/cli/templates/provider/Dockerfile.tpl +7 -0
  18. package/src/cli/templates/provider/README.md.tpl +41 -0
  19. package/src/cli/templates/provider/dev.ts.tpl +5 -0
  20. package/src/cli/templates/provider/index.test.ts.tpl +13 -0
  21. package/src/cli/templates/provider/index.ts.tpl +58 -0
  22. package/src/cli/templates/provider/start.ts.tpl +5 -0
  23. package/src/config/loader.ts +61 -1
  24. package/src/define.ts +565 -41
  25. package/src/dev.ts +2 -6
  26. package/src/errors.ts +42 -0
  27. package/src/index.ts +44 -38
  28. package/src/lint.ts +574 -0
  29. package/src/provider.ts +13 -0
  30. package/src/runtime/auth-flow.ts +67 -0
  31. package/src/runtime/credential.ts +95 -0
  32. package/src/runtime/env.ts +13 -0
  33. package/src/runtime/executor.ts +13 -14
  34. package/src/runtime/http.ts +36 -12
  35. package/src/runtime/insights.ts +3 -3
  36. package/src/runtime/key-derivation.ts +122 -0
  37. package/src/runtime/keyring.ts +148 -0
  38. package/src/runtime/namespace.ts +33 -0
  39. package/src/runtime/prevalidate.ts +252 -0
  40. package/src/runtime/tls.ts +41 -17
  41. package/src/runtime/waterfall.ts +0 -1
  42. package/src/schema.ts +77 -0
  43. package/src/serve.ts +1 -664
  44. package/src/server/index.ts +22 -0
  45. package/src/server/serve.ts +624 -0
  46. package/src/server/types.ts +78 -0
  47. package/src/stealth/profiles.ts +10 -93
  48. package/src/testing/run.ts +391 -32
  49. package/src/types.ts +390 -41
  50. package/bin/apifuse-init.ts +0 -387
  51. package/src/__tests__/auth.test.ts +0 -396
  52. package/src/__tests__/browser-auth.test.ts +0 -180
  53. package/src/__tests__/browser.test.ts +0 -632
  54. package/src/__tests__/define.test.ts +0 -225
  55. package/src/__tests__/errors.test.ts +0 -69
  56. package/src/__tests__/executor.test.ts +0 -214
  57. package/src/__tests__/http.test.ts +0 -238
  58. package/src/__tests__/insights.test.ts +0 -210
  59. package/src/__tests__/instrumentation.test.ts +0 -290
  60. package/src/__tests__/otlp.test.ts +0 -141
  61. package/src/__tests__/perf.test.ts +0 -60
  62. package/src/__tests__/providers-yaml.test.ts +0 -135
  63. package/src/__tests__/proxy.test.ts +0 -359
  64. package/src/__tests__/recipes.test.ts +0 -36
  65. package/src/__tests__/serve.test.ts +0 -233
  66. package/src/__tests__/session.test.ts +0 -231
  67. package/src/__tests__/state.test.ts +0 -100
  68. package/src/__tests__/stealth.test.ts +0 -57
  69. package/src/__tests__/testing.test.ts +0 -97
  70. package/src/__tests__/tls.test.ts +0 -345
  71. package/src/__tests__/types.test.ts +0 -142
  72. package/src/__tests__/utils.test.ts +0 -62
  73. package/src/__tests__/waterfall.test.ts +0 -270
  74. package/src/config/providers-yaml.ts +0 -370
  75. package/src/index.test.ts +0 -1
  76. package/src/protocol.ts +0 -183
  77. package/src/runtime/auth.ts +0 -245
  78. package/src/runtime/session.ts +0 -573
  79. package/src/runtime/state.ts +0 -124
@@ -1,270 +0,0 @@
1
- import { describe, expect, it } from "bun:test";
2
-
3
- import type { Span } from "../runtime/trace";
4
- import { renderWaterfall, type WaterfallRequest } from "../runtime/waterfall";
5
-
6
- function assertDefined<T>(value: T | null | undefined, message?: string): T {
7
- if (value === null || value === undefined) {
8
- throw new Error(message ?? "Expected value to be defined");
9
- }
10
-
11
- return value;
12
- }
13
-
14
- function makeSpan(overrides: Partial<Span> & { name: string }): Span {
15
- return {
16
- id: crypto.randomUUID(),
17
- name: overrides.name,
18
- startedAt: overrides.startedAt ?? 1000,
19
- endedAt: overrides.endedAt ?? 1100,
20
- duration_ms: overrides.duration_ms ?? 100,
21
- status: overrides.status ?? "ok",
22
- attributes: overrides.attributes ?? {},
23
- ...(overrides.parentId ? { parentId: overrides.parentId } : {}),
24
- ...(overrides.error ? { error: overrides.error } : {}),
25
- ...(overrides.id ? { id: overrides.id } : {}),
26
- };
27
- }
28
-
29
- const defaultRequest: WaterfallRequest = {
30
- method: "GET",
31
- path: "/v1/coingecko/prices",
32
- status: 200,
33
- totalMs: 289,
34
- };
35
-
36
- describe("renderWaterfall", () => {
37
- it("renders span name and duration", () => {
38
- const rootSpan = makeSpan({
39
- id: "root-1",
40
- name: "prices",
41
- startedAt: 1000,
42
- endedAt: 1289,
43
- duration_ms: 289,
44
- });
45
-
46
- const child = makeSpan({
47
- name: "normalizeRequest",
48
- parentId: "root-1",
49
- startedAt: 1000,
50
- endedAt: 1001,
51
- duration_ms: 0.1,
52
- });
53
-
54
- const output = renderWaterfall([rootSpan, child], defaultRequest);
55
-
56
- expect(output).toContain("prices");
57
- expect(output).toContain("289ms");
58
- expect(output).toContain("normalizeRequest");
59
- expect(output).toContain("0.1ms");
60
- });
61
-
62
- it("indents child spans", () => {
63
- const root = makeSpan({
64
- id: "root-1",
65
- name: "prices",
66
- startedAt: 1000,
67
- endedAt: 1289,
68
- duration_ms: 289,
69
- });
70
-
71
- const parent = makeSpan({
72
- id: "parent-1",
73
- name: "tls.fetch",
74
- parentId: "root-1",
75
- startedAt: 1001,
76
- endedAt: 1286,
77
- duration_ms: 285,
78
- });
79
-
80
- const child = makeSpan({
81
- name: "dns",
82
- parentId: "parent-1",
83
- startedAt: 1001,
84
- endedAt: 1012,
85
- duration_ms: 11.3,
86
- });
87
-
88
- const output = renderWaterfall([root, parent, child], defaultRequest);
89
-
90
- const lines = output.split("\n");
91
-
92
- const tlsFetchLine = lines.find((l) => l.includes("tls.fetch"));
93
- const dnsLine = lines.find((l) => l.includes("dns"));
94
-
95
- expect(tlsFetchLine).toBeDefined();
96
- expect(dnsLine).toBeDefined();
97
-
98
- const stripAnsi = (s: string) =>
99
- s.replace(new RegExp("\\u001b\\[[0-9;]*m", "g"), "");
100
- const tlsFetchPos = stripAnsi(assertDefined(tlsFetchLine)).indexOf("├─");
101
- const dnsPos = stripAnsi(assertDefined(dnsLine)).indexOf("└─");
102
-
103
- expect(dnsPos).toBeGreaterThan(tlsFetchPos);
104
- });
105
-
106
- it("marks slow spans yellow", () => {
107
- const root = makeSpan({
108
- id: "root-1",
109
- name: "prices",
110
- startedAt: 1000,
111
- endedAt: 1600,
112
- duration_ms: 600,
113
- });
114
-
115
- const slowChild = makeSpan({
116
- name: "tls.fetch",
117
- parentId: "root-1",
118
- startedAt: 1000,
119
- endedAt: 1600,
120
- duration_ms: 600,
121
- });
122
-
123
- const output = renderWaterfall(
124
- [root, slowChild],
125
- {
126
- ...defaultRequest,
127
- totalMs: 600,
128
- },
129
- { slowThresholdMs: 500 },
130
- );
131
-
132
- expect(output).toContain("\x1b[33m");
133
- });
134
-
135
- it("marks error spans red", () => {
136
- const root = makeSpan({
137
- id: "root-1",
138
- name: "prices",
139
- startedAt: 1000,
140
- endedAt: 1100,
141
- duration_ms: 100,
142
- status: "error",
143
- error: "Network failure",
144
- });
145
-
146
- const output = renderWaterfall([root], {
147
- ...defaultRequest,
148
- status: 500,
149
- totalMs: 100,
150
- });
151
-
152
- expect(output).toContain("\x1b[31m");
153
- });
154
-
155
- it("marks bottleneck with star", () => {
156
- const root = makeSpan({
157
- id: "root-1",
158
- name: "prices",
159
- startedAt: 1000,
160
- endedAt: 1289,
161
- duration_ms: 289,
162
- });
163
-
164
- const fast = makeSpan({
165
- name: "normalizeRequest",
166
- parentId: "root-1",
167
- startedAt: 1000,
168
- endedAt: 1001,
169
- duration_ms: 0.1,
170
- });
171
-
172
- const slow = makeSpan({
173
- name: "tls.fetch",
174
- parentId: "root-1",
175
- startedAt: 1001,
176
- endedAt: 1286,
177
- duration_ms: 285,
178
- });
179
-
180
- const transform = makeSpan({
181
- name: "transformResponse",
182
- parentId: "root-1",
183
- startedAt: 1286,
184
- endedAt: 1289,
185
- duration_ms: 3.2,
186
- });
187
-
188
- const output = renderWaterfall(
189
- [root, fast, slow, transform],
190
- defaultRequest,
191
- );
192
-
193
- const lines = output.split("\n");
194
- const tlsFetchLine = lines.find((l) => l.includes("tls.fetch"));
195
- expect(tlsFetchLine).toContain("★");
196
-
197
- const normalizeLine = lines.find((l) => l.includes("normalizeRequest"));
198
- expect(normalizeLine).not.toContain("★");
199
- });
200
-
201
- it("returns empty string for no spans", () => {
202
- const output = renderWaterfall([], defaultRequest);
203
- expect(output).toBe("");
204
- });
205
-
206
- it("renders status line with method and path", () => {
207
- const root = makeSpan({
208
- id: "root-1",
209
- name: "prices",
210
- duration_ms: 100,
211
- });
212
-
213
- const output = renderWaterfall([root], defaultRequest);
214
-
215
- expect(output).toContain("GET");
216
- expect(output).toContain("/v1/coingecko/prices");
217
- expect(output).toContain("200");
218
- expect(output).toContain("OK");
219
- });
220
-
221
- it("renders timing bars proportional to total duration", () => {
222
- const root = makeSpan({
223
- id: "root-1",
224
- name: "op",
225
- startedAt: 1000,
226
- endedAt: 1200,
227
- duration_ms: 200,
228
- });
229
-
230
- const half = makeSpan({
231
- name: "half",
232
- parentId: "root-1",
233
- startedAt: 1000,
234
- endedAt: 1100,
235
- duration_ms: 100,
236
- });
237
-
238
- const full = makeSpan({
239
- name: "full",
240
- parentId: "root-1",
241
- startedAt: 1100,
242
- endedAt: 1200,
243
- duration_ms: 200,
244
- });
245
-
246
- const output = renderWaterfall(
247
- [root, half, full],
248
- {
249
- ...defaultRequest,
250
- totalMs: 200,
251
- },
252
- { maxBarWidth: 20 },
253
- );
254
-
255
- const stripAnsi = (s: string) =>
256
- s.replace(new RegExp("\\u001b\\[[0-9;]*m", "g"), "");
257
- const lines = output.split("\n").map(stripAnsi);
258
-
259
- const halfLine = lines.find(
260
- (l) => l.includes("half") && !l.includes("full"),
261
- );
262
- const fullLine = lines.find((l) => l.includes("full"));
263
-
264
- const countBars = (s: string) => (s.match(/━/g) ?? []).length;
265
- const halfBars = countBars(halfLine ?? "");
266
- const fullBars = countBars(fullLine ?? "");
267
-
268
- expect(fullBars).toBeGreaterThan(halfBars);
269
- });
270
- });
@@ -1,370 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
-
3
- import { z } from "zod";
4
-
5
- import { SDKError, ValidationError } from "../errors";
6
-
7
- type YamlObject = Record<string, unknown>;
8
-
9
- function stripInlineComment(line: string): string {
10
- let inSingleQuote = false;
11
- let inDoubleQuote = false;
12
-
13
- for (let index = 0; index < line.length; index += 1) {
14
- const char = line[index];
15
-
16
- if (char === "'" && !inDoubleQuote) {
17
- inSingleQuote = !inSingleQuote;
18
- continue;
19
- }
20
-
21
- if (char === '"' && !inSingleQuote) {
22
- inDoubleQuote = !inDoubleQuote;
23
- continue;
24
- }
25
-
26
- if (char === "#" && !inSingleQuote && !inDoubleQuote) {
27
- return line.slice(0, index).trimEnd();
28
- }
29
- }
30
-
31
- return line;
32
- }
33
-
34
- function parseQuotedString(value: string): string {
35
- if (
36
- (value.startsWith('"') && value.endsWith('"')) ||
37
- (value.startsWith("'") && value.endsWith("'"))
38
- ) {
39
- return value.slice(1, -1);
40
- }
41
-
42
- return value;
43
- }
44
-
45
- function splitFlowArrayItems(value: string): string[] {
46
- const items: string[] = [];
47
- let current = "";
48
- let inSingleQuote = false;
49
- let inDoubleQuote = false;
50
-
51
- for (let index = 0; index < value.length; index += 1) {
52
- const char = value[index];
53
-
54
- if (char === "'" && !inDoubleQuote) {
55
- inSingleQuote = !inSingleQuote;
56
- current += char;
57
- continue;
58
- }
59
-
60
- if (char === '"' && !inSingleQuote) {
61
- inDoubleQuote = !inDoubleQuote;
62
- current += char;
63
- continue;
64
- }
65
-
66
- if (char === "," && !inSingleQuote && !inDoubleQuote) {
67
- items.push(current.trim());
68
- current = "";
69
- continue;
70
- }
71
-
72
- current += char;
73
- }
74
-
75
- if (current.trim()) {
76
- items.push(current.trim());
77
- }
78
-
79
- return items;
80
- }
81
-
82
- function parseYamlScalar(value: string): unknown {
83
- if (value === "{}") {
84
- return {};
85
- }
86
-
87
- if (value === "[]") {
88
- return [];
89
- }
90
-
91
- if (value.startsWith("[") && value.endsWith("]")) {
92
- const inner = value.slice(1, -1).trim();
93
- if (!inner) {
94
- return [];
95
- }
96
-
97
- return splitFlowArrayItems(inner).map((item) => parseYamlScalar(item));
98
- }
99
-
100
- if (value === "true") {
101
- return true;
102
- }
103
-
104
- if (value === "false") {
105
- return false;
106
- }
107
-
108
- if (/^-?\d+$/.test(value)) {
109
- return Number.parseInt(value, 10);
110
- }
111
-
112
- return parseQuotedString(value);
113
- }
114
-
115
- function parseYamlObject(yamlContent: string): YamlObject {
116
- const root: YamlObject = {};
117
- const stack: Array<{ container: YamlObject; indent: number }> = [
118
- { container: root, indent: -1 },
119
- ];
120
-
121
- for (const rawLine of yamlContent.split(/\r?\n/u)) {
122
- const withoutComment = stripInlineComment(rawLine);
123
- if (!withoutComment.trim()) {
124
- continue;
125
- }
126
-
127
- const indent = withoutComment.match(/^\s*/u)?.[0].length ?? 0;
128
- const line = withoutComment.trim();
129
- const separatorIndex = line.indexOf(":");
130
-
131
- if (separatorIndex === -1) {
132
- throw new Error(`Invalid YAML line: ${rawLine}`);
133
- }
134
-
135
- const key = line.slice(0, separatorIndex).trim();
136
- const rawValue = line.slice(separatorIndex + 1).trim();
137
-
138
- while (stack.length > 1 && indent <= stack[stack.length - 1]?.indent) {
139
- stack.pop();
140
- }
141
-
142
- const parent = stack[stack.length - 1]?.container;
143
- if (!parent) {
144
- throw new Error(`Could not resolve YAML parent for line: ${rawLine}`);
145
- }
146
-
147
- if (!rawValue) {
148
- const child: YamlObject = {};
149
- parent[key] = child;
150
- stack.push({ container: child, indent });
151
- continue;
152
- }
153
-
154
- parent[key] = parseYamlScalar(rawValue);
155
- }
156
-
157
- return root;
158
- }
159
-
160
- const PoolSizeRangeInputSchema = z
161
- .string()
162
- .regex(/^\d+\.\.\d+$/, "pool-size must use min..max format")
163
- .transform((value, ctx) => {
164
- const [minRaw, maxRaw] = value.split("..");
165
- const min = Number.parseInt(minRaw, 10);
166
- const max = Number.parseInt(maxRaw, 10);
167
-
168
- if (min > max) {
169
- ctx.addIssue({
170
- code: z.ZodIssueCode.custom,
171
- message: "pool-size min must be less than or equal to max",
172
- });
173
- return z.NEVER;
174
- }
175
-
176
- return { max, min };
177
- });
178
-
179
- const PoolSizeRangeSchema = z.object({
180
- max: z.number().int(),
181
- min: z.number().int(),
182
- });
183
-
184
- const SecurityOverrideSchema = z
185
- .object({
186
- "read-only-rootfs": z.boolean().optional(),
187
- "no-new-privileges": z.boolean().optional(),
188
- egress: z.array(z.string()).optional(),
189
- env: z.array(z.string()).optional(),
190
- })
191
- .passthrough();
192
-
193
- const ContainerOverrideSchema = z
194
- .object({
195
- image: z.string().optional(),
196
- memory: z.string().optional(),
197
- cpu: z.string().optional(),
198
- timeout: z.string().optional(),
199
- "restart-policy": z.enum(["on-failure", "always", "never"]).optional(),
200
- "max-restarts": z.number().int().optional(),
201
- })
202
- .passthrough();
203
-
204
- const CdpOverrideSchema = z
205
- .object({
206
- "pool-size": PoolSizeRangeInputSchema,
207
- "page-timeout": z.string().optional(),
208
- "pages-per-instance": z.number().int().optional(),
209
- "idle-timeout": z.string().optional(),
210
- "chrome-flags": z.array(z.string()).optional(),
211
- })
212
- .passthrough();
213
-
214
- const DefaultsSchema = z
215
- .object({
216
- container: ContainerOverrideSchema.optional(),
217
- security: SecurityOverrideSchema.optional(),
218
- })
219
- .passthrough();
220
-
221
- const ProviderConfigOverrideSchema = z
222
- .object({
223
- container: ContainerOverrideSchema.optional(),
224
- runtime: z.enum(["standard", "browser"]).optional(),
225
- replicas: z.number().int().optional(),
226
- security: SecurityOverrideSchema.optional(),
227
- cdp: CdpOverrideSchema.optional(),
228
- })
229
- .passthrough();
230
-
231
- export const SecuritySchema = z
232
- .object({
233
- "read-only-rootfs": z.boolean().default(true),
234
- "no-new-privileges": z.boolean().default(true),
235
- egress: z.array(z.string()).optional(),
236
- env: z.array(z.string()).optional(),
237
- })
238
- .passthrough();
239
-
240
- export const ContainerSchema = z
241
- .object({
242
- image: z.string().default("auto"),
243
- memory: z.string().default("256Mi"),
244
- cpu: z.string().default("250m"),
245
- timeout: z.string().default("30s"),
246
- "restart-policy": z
247
- .enum(["on-failure", "always", "never"])
248
- .default("on-failure"),
249
- "max-restarts": z.number().int().default(3),
250
- })
251
- .passthrough();
252
-
253
- export const CdpSchema = z
254
- .object({
255
- "pool-size": PoolSizeRangeSchema,
256
- "page-timeout": z.string().default("120s"),
257
- "pages-per-instance": z.number().int().default(4),
258
- "idle-timeout": z.string().default("5m"),
259
- "chrome-flags": z.array(z.string()).optional(),
260
- })
261
- .passthrough();
262
-
263
- export const ProviderConfigSchema = z
264
- .object({
265
- container: ContainerSchema,
266
- runtime: z.enum(["standard", "browser"]).default("standard"),
267
- replicas: z.number().int().default(1),
268
- security: SecuritySchema,
269
- cdp: CdpSchema.optional(),
270
- })
271
- .passthrough();
272
-
273
- export const ProvidersYamlSchema = z
274
- .object({
275
- defaults: DefaultsSchema.optional(),
276
- providers: z.record(z.string(), ProviderConfigOverrideSchema),
277
- })
278
- .passthrough();
279
-
280
- export type PoolSizeRange = z.infer<typeof PoolSizeRangeSchema>;
281
- export type ProvidersYaml = z.infer<typeof ProvidersYamlSchema>;
282
- export type ProviderConfig = z.infer<typeof ProviderConfigSchema>;
283
-
284
- function toValidationError(error: unknown): ValidationError {
285
- if (error instanceof z.ZodError) {
286
- return new ValidationError("Invalid providers.yaml configuration", {
287
- code: "INVALID_CONNECTORS_YAML",
288
- fix: "Match the providers.yaml schema from openspec/provider-sdk/08-infrastructure.md §8.2.",
289
- zodError: error,
290
- });
291
- }
292
-
293
- return new ValidationError("Invalid providers.yaml configuration", {
294
- code: "INVALID_CONNECTORS_YAML",
295
- cause: error instanceof Error ? error : undefined,
296
- fix: "Check providers.yaml syntax and field values.",
297
- });
298
- }
299
-
300
- export function parseProvidersYaml(yamlContent: string): ProvidersYaml {
301
- try {
302
- const parsed = parseYamlObject(yamlContent);
303
- return ProvidersYamlSchema.parse(parsed);
304
- } catch (error) {
305
- if (error instanceof z.ZodError) {
306
- throw toValidationError(error);
307
- }
308
-
309
- throw new SDKError("Failed to parse providers.yaml", {
310
- code: "CONNECTORS_YAML_PARSE_FAILED",
311
- cause: error instanceof Error ? error : undefined,
312
- fix: "Ensure providers.yaml contains valid YAML.",
313
- });
314
- }
315
- }
316
-
317
- export async function loadProvidersYaml(
318
- filePath: string,
319
- ): Promise<ProvidersYaml> {
320
- try {
321
- const yamlContent = await readFile(filePath, "utf8");
322
- return parseProvidersYaml(yamlContent);
323
- } catch (error) {
324
- if (error instanceof SDKError || error instanceof ValidationError) {
325
- throw error;
326
- }
327
-
328
- throw new SDKError(`Failed to load providers.yaml from ${filePath}`, {
329
- code: "CONNECTORS_YAML_LOAD_FAILED",
330
- cause: error instanceof Error ? error : undefined,
331
- fix: "Verify the file exists and is readable.",
332
- });
333
- }
334
- }
335
-
336
- export function resolveProviderConfig(
337
- yaml: ProvidersYaml,
338
- providerId: string,
339
- ): ProviderConfig {
340
- const providerOverride = yaml.providers[providerId];
341
-
342
- if (!providerOverride) {
343
- throw new SDKError(`Provider "${providerId}" not found in providers.yaml`, {
344
- code: "CONNECTOR_NOT_FOUND",
345
- fix: `Add ${providerId} under providers: in providers.yaml.`,
346
- });
347
- }
348
-
349
- const container = ContainerSchema.parse({
350
- ...yaml.defaults?.container,
351
- ...providerOverride.container,
352
- });
353
-
354
- const security = SecuritySchema.parse({
355
- ...yaml.defaults?.security,
356
- ...providerOverride.security,
357
- });
358
-
359
- const resolved = {
360
- container,
361
- security,
362
- runtime: providerOverride.runtime ?? "standard",
363
- replicas: providerOverride.replicas ?? 1,
364
- cdp: providerOverride.cdp
365
- ? CdpSchema.parse(providerOverride.cdp)
366
- : undefined,
367
- };
368
-
369
- return ProviderConfigSchema.parse(resolved);
370
- }
package/src/index.test.ts DELETED
@@ -1 +0,0 @@
1
- // placeholder to keep bun test from erroring on zero test files