@sdk-it/typescript 0.12.10 → 0.12.11

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/index.js CHANGED
@@ -57,7 +57,7 @@ type ${spec.name}Options = z.infer<typeof optionsSchema>;
57
57
  export class ${spec.name} {
58
58
  public options: ${spec.name}Options
59
59
  constructor(options: ${spec.name}Options) {
60
- this.options = options;
60
+ this.options = optionsSchema.parse(options);
61
61
  }
62
62
 
63
63
  async request<E extends keyof Endpoints>(
@@ -100,7 +100,8 @@ var SchemaEndpoint = class {
100
100
  `import z from 'zod';`,
101
101
  'import type { Endpoints } from "./endpoints.ts";',
102
102
  `import { toRequest, json, urlencoded, nobody, formdata, createUrl } from './http/request.ts';`,
103
- `import type { ParseError } from './http/parser.ts';`
103
+ `import type { ParseError } from './http/parser.ts';`,
104
+ 'import { chunked, buffered } from "./http/parse-response.ts";'
104
105
  ];
105
106
  #endpoints = [];
106
107
  addEndpoint(endpoint, operation) {
@@ -118,7 +119,7 @@ ${this.#endpoints.join("\n")}
118
119
  };
119
120
  var Emitter = class {
120
121
  imports = [
121
- `import z from 'zod';`,
122
+ `import type z from 'zod';`,
122
123
  `import type { ParseError } from './http/parser.ts';`
123
124
  ];
124
125
  endpoints = [];
@@ -184,7 +185,7 @@ function generateSDK(spec) {
184
185
  const errors = [];
185
186
  for (const [name, operations] of Object.entries(spec.operations)) {
186
187
  emitter.addImport(
187
- `import * as ${camelcase(name)} from './inputs/${spinalcase(name)}.ts';`
188
+ `import type * as ${camelcase(name)} from './inputs/${spinalcase(name)}.ts';`
188
189
  );
189
190
  schemaEndpoint.addImport(
190
191
  `import * as ${camelcase(name)} from './inputs/${spinalcase(name)}.ts';`
@@ -236,9 +237,10 @@ function generateSDK(spec) {
236
237
  endpoint,
237
238
  `{
238
239
  schema: ${schemaRef}${addTypeParser ? `.${type}` : ""},
240
+ deserializer: ${operation.parser === "chunked" ? "chunked" : "buffered"},
239
241
  toRequest(input: Endpoints['${endpoint}']['input']) {
240
242
  const endpoint = '${endpoint}';
241
- return toRequest(endpoint, ${operation.contentType || "nobody"}(input, {
243
+ return toRequest(endpoint, ${operation.outgoingContentType || "nobody"}(input, {
242
244
  inputHeaders: [${inputHeaders}],
243
245
  inputQuery: [${inputQuery}],
244
246
  inputBody: [${inputBody}],
@@ -342,8 +344,8 @@ function importsToString(...imports) {
342
344
  throw new Error(`Invalid import ${JSON.stringify(it)}`);
343
345
  });
344
346
  }
345
- function exclude2(list, exclude3) {
346
- return list.filter((it) => !exclude3.includes(it));
347
+ function exclude(list, exclude2) {
348
+ return list.filter((it) => !exclude2.includes(it));
347
349
  }
348
350
  function useImports(content, imports) {
349
351
  const output = [];
@@ -693,10 +695,20 @@ var ZodDeserialzer = class {
693
695
  }
694
696
  allOf(schemas) {
695
697
  const allOfSchemas = schemas.map((sub) => this.handle(sub, true));
698
+ if (allOfSchemas.length === 0) {
699
+ return `z.unknown()`;
700
+ }
696
701
  if (allOfSchemas.length === 1) {
697
702
  return allOfSchemas[0];
698
703
  }
699
- return allOfSchemas.length ? `z.intersection(${allOfSchemas.join(", ")})` : allOfSchemas[0];
704
+ return this.#toIntersection(allOfSchemas);
705
+ }
706
+ #toIntersection(schemas) {
707
+ const [left, ...right] = schemas;
708
+ if (!right.length) {
709
+ return left;
710
+ }
711
+ return `z.intersection(${left}, ${this.#toIntersection(right)})`;
700
712
  }
701
713
  anyOf(schemas, required) {
702
714
  const anyOfSchemas = schemas.map((sub) => this.handle(sub, false));
@@ -901,7 +913,7 @@ function generateCode(config) {
901
913
  const formatOperationId = config.operationId ?? defaults.operationId;
902
914
  const operationName = formatOperationId(operation, path, method);
903
915
  console.log(`Processing ${method} ${path}`);
904
- const groupName = (operation.tags ?? ["unknown"])[0];
916
+ const [groupName] = Array.isArray(operation.tags) ? operation.tags : ["unknown"];
905
917
  groups[groupName] ??= [];
906
918
  const inputs = {};
907
919
  const additionalProperties = [];
@@ -942,7 +954,7 @@ function generateCode(config) {
942
954
  "application/xml": "xml",
943
955
  "text/plain": "text"
944
956
  };
945
- let contentType;
957
+ let outgoingContentType;
946
958
  if (operation.requestBody && Object.keys(operation.requestBody).length) {
947
959
  const content = isRef(operation.requestBody) ? get2(followRef(config.spec, operation.requestBody.$ref), ["content"]) : operation.requestBody.content;
948
960
  for (const type in content) {
@@ -961,22 +973,17 @@ function generateCode(config) {
961
973
  {}
962
974
  )
963
975
  });
964
- for (const [name] of Object.entries(ctSchema.properties ?? {})) {
965
- inputs[name] = {
966
- in: "body",
967
- schema: ""
968
- };
969
- }
976
+ Object.assign(inputs, bodyInputs(config, ctSchema));
970
977
  types[shortContenTypeMap[type]] = zodDeserialzer.handle(schema, true);
971
978
  }
972
979
  if (content["application/json"]) {
973
- contentType = "json";
980
+ outgoingContentType = "json";
974
981
  } else if (content["application/x-www-form-urlencoded"]) {
975
- contentType = "urlencoded";
982
+ outgoingContentType = "urlencoded";
976
983
  } else if (content["multipart/form-data"]) {
977
- contentType = "formdata";
984
+ outgoingContentType = "formdata";
978
985
  } else {
979
- contentType = "json";
986
+ outgoingContentType = "json";
980
987
  }
981
988
  } else {
982
989
  const properties = additionalProperties.reduce(
@@ -999,6 +1006,7 @@ function generateCode(config) {
999
1006
  operation.responses ??= {};
1000
1007
  let foundResponse = false;
1001
1008
  const output = [`import z from 'zod';`];
1009
+ let parser = "buffered";
1002
1010
  for (const status in operation.responses) {
1003
1011
  const response = operation.responses[status];
1004
1012
  const statusCode = +status;
@@ -1009,6 +1017,9 @@ function generateCode(config) {
1009
1017
  foundResponse = true;
1010
1018
  const responseContent = get2(response, ["content"]);
1011
1019
  const isJson = responseContent && responseContent["application/json"];
1020
+ if ((response.headers ?? {})["Transfer-Encoding"]) {
1021
+ parser = "chunked";
1022
+ }
1012
1023
  const imports = [];
1013
1024
  const typeScriptDeserialzer = new TypeScriptDeserialzer(
1014
1025
  config.spec,
@@ -1044,8 +1055,9 @@ function generateCode(config) {
1044
1055
  type: "http",
1045
1056
  inputs,
1046
1057
  errors: errors.length ? errors : ["ServerError"],
1047
- contentType,
1058
+ outgoingContentType,
1048
1059
  schemas: types,
1060
+ parser,
1049
1061
  formatOutput: () => ({
1050
1062
  import: pascalcase(operationName + " output"),
1051
1063
  use: pascalcase(operationName + " output")
@@ -1059,12 +1071,42 @@ function generateCode(config) {
1059
1071
  }
1060
1072
  return { groups, commonSchemas, commonZod, outputs };
1061
1073
  }
1074
+ function toProps(spec, schemaOrRef, aggregator = []) {
1075
+ if (isRef(schemaOrRef)) {
1076
+ const schema = followRef(spec, schemaOrRef.$ref);
1077
+ return toProps(spec, schema, aggregator);
1078
+ } else if (schemaOrRef.type === "object") {
1079
+ for (const [name] of Object.entries(schemaOrRef.properties ?? {})) {
1080
+ aggregator.push(name);
1081
+ }
1082
+ return void 0;
1083
+ } else if (schemaOrRef.allOf) {
1084
+ for (const it of schemaOrRef.allOf) {
1085
+ toProps(spec, it, aggregator);
1086
+ }
1087
+ return void 0;
1088
+ }
1089
+ }
1090
+ function bodyInputs(config, ctSchema) {
1091
+ const props = [];
1092
+ toProps(config.spec, ctSchema, props);
1093
+ return props.reduce(
1094
+ (acc, prop) => ({
1095
+ ...acc,
1096
+ [prop]: {
1097
+ in: "body",
1098
+ schema: ""
1099
+ }
1100
+ }),
1101
+ {}
1102
+ );
1103
+ }
1062
1104
 
1063
1105
  // packages/typescript/src/lib/http/interceptors.txt
1064
- var interceptors_default = "export interface Interceptor {\n before?: (request: Request) => Promise<Request> | Request;\n after?: (response: Response) => Promise<Response> | Response;\n}\n\nexport const createDefaultHeadersInterceptor = (\n getHeaders: () => Record<string, string | undefined>,\n) => {\n return {\n before(request: Request) {\n const headers = getHeaders();\n\n for (const [key, value] of Object.entries(headers)) {\n // Only set the header if it doesn't already exist and has a value\n if (value !== undefined && !request.headers.has(key)) {\n request.headers.set(key, value);\n }\n }\n\n return request;\n },\n };\n};\n\nexport const createBaseUrlInterceptor = (getBaseUrl: () => string) => {\n return {\n before(request: Request) {\n const baseUrl = getBaseUrl();\n if (request.url.startsWith('local://')) {\n return new Request(request.url.replace('local://', baseUrl), request);\n }\n return request;\n },\n };\n};\n\nexport const logInterceptor = {\n before(request: Request) {\n console.log('Request', request);\n return request;\n },\n after(response: Response) {\n console.log('Response', response);\n return response;\n },\n};\n";
1106
+ var interceptors_default = "export interface Interceptor {\n before?: (request: Request) => Promise<Request> | Request;\n after?: (response: Response) => Promise<Response> | Response;\n}\n\nexport const createDefaultHeadersInterceptor = (\n getHeaders: () => Record<string, string | undefined>,\n) => {\n return {\n before(request: Request) {\n const headers = getHeaders();\n\n for (const [key, value] of Object.entries(headers)) {\n // Only set the header if it doesn't already exist and has a value\n if (value !== undefined && !request.headers.has(key)) {\n request.headers.set(key, value);\n }\n }\n\n return request;\n },\n };\n};\n\nexport const createBaseUrlInterceptor = (getBaseUrl: () => string) => {\n return {\n before(request: Request) {\n const baseUrl = getBaseUrl();\n if (request.url.startsWith('local://')) {\n return new Request(request.url.replace('local://', baseUrl), request);\n }\n return request;\n },\n };\n};\n\nexport const logInterceptor = {\n before(request: Request) {\n console.log('Request', request);\n return request;\n },\n after(response: Response) {\n console.log('Response', response);\n return response;\n },\n};\n\n/**\n * Creates an interceptor that logs detailed information about requests and responses.\n * @param options Configuration options for the logger\n * @returns An interceptor object with before and after handlers\n */\nexport const createDetailedLogInterceptor = (options?: {\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n includeRequestBody?: boolean;\n includeResponseBody?: boolean;\n}) => {\n const logLevel = options?.logLevel || 'info';\n const includeRequestBody = options?.includeRequestBody || false;\n const includeResponseBody = options?.includeResponseBody || false;\n\n return {\n async before(request: Request) {\n const logData = {\n url: request.url,\n method: request.method,\n contentType: request.headers.get('Content-Type'),\n headers: Object.fromEntries([...request.headers.entries()]),\n };\n\n console[logLevel]('\u{1F680} Outgoing Request:', logData);\n\n if (includeRequestBody) {\n try {\n // Clone the request to avoid consuming the body stream\n const clonedRequest = request.clone();\n if (clonedRequest.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedRequest.json().catch(() => null);\n console[logLevel]('Request Body:', body);\n } else {\n const body = await clonedRequest.text().catch(() => null);\n console[logLevel]('Request Body:', body);\n }\n } catch (error) {\n console.error('Could not log request body:', error);\n }\n }\n\n return request;\n },\n\n async after(response: Response) {\n const logData = {\n status: response.status,\n statusText: response.statusText,\n url: response.url,\n headers: Object.fromEntries([...response.headers.entries()]),\n };\n\n console[logLevel]('\u{1F4E5} Incoming Response:', logData);\n\n if (includeResponseBody && response.body) {\n try {\n // Clone the response to avoid consuming the body stream\n const clonedResponse = response.clone();\n if (clonedResponse.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedResponse.json().catch(() => null);\n console[logLevel]('Response Body:', body);\n } else {\n const body = await clonedResponse.text().catch(() => null);\n console[logLevel]('Response Body:', body.substring(0, 500) + (body.length > 500 ? '...' : ''));\n }\n } catch (error) {\n console.error('Could not log response body:', error);\n }\n }\n\n return response;\n },\n };\n};\n";
1065
1107
 
1066
1108
  // packages/typescript/src/lib/http/parse-response.txt
1067
- var parse_response_default = "import { parse } from 'fast-content-type-parse';\n\nexport async function handleError(response: Response) {\n try {\n if (response.status >= 400 && response.status < 500) {\n const body = (await response.json()) as Record<string, any>;\n return {\n status: response.status,\n body: body,\n };\n }\n return new Error(\n `An error occurred while fetching the data. Status: ${response.status}`,\n );\n } catch (error) {\n return error as any;\n }\n}\n\nasync function handleChunkedResponse(response: Response, contentType: string) {\n const { type } = parse(contentType);\n\n switch (type) {\n case 'application/json': {\n let buffer = '';\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return JSON.parse(buffer);\n }\n case 'text/html':\n case 'text/plain': {\n let buffer = '';\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return buffer;\n }\n default:\n return response.body;\n }\n}\n\nexport async function parseResponse(response: Response) {\n const contentType = response.headers.get('Content-Type');\n if (!contentType) {\n throw new Error('Content-Type header is missing');\n }\n\n if (response.status === 204) {\n return null;\n }\n const isChunked = response.headers.get('Transfer-Encoding') === 'chunked';\n if (isChunked) {\n return response.body!;\n // return handleChunkedResponse(response, contentType);\n }\n\n const { type } = parse(contentType);\n switch (type) {\n case 'application/json':\n return response.json();\n case 'text/plain':\n return response.text();\n case 'text/html':\n return response.text();\n case 'text/xml':\n case 'application/xml':\n return response.text();\n case 'application/x-www-form-urlencoded': {\n const text = await response.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n case 'multipart/form-data':\n return response.formData();\n default:\n throw new Error(`Unsupported content type: ${contentType}`);\n }\n}\n";
1109
+ var parse_response_default = 'import { parse } from "fast-content-type-parse";\n\nexport async function handleError(response: Response) {\n try {\n if (response.status >= 400 && response.status < 500) {\n const body = (await response.json()) as Record<string, any>;\n return {\n status: response.status,\n body: body,\n };\n }\n return new Error(\n `An error occurred while fetching the data. Status: ${response.status}`,\n );\n } catch (error) {\n return error as any;\n }\n}\n\nasync function handleChunkedResponse(response: Response, contentType: string) {\n const { type } = parse(contentType);\n\n switch (type) {\n case "application/json": {\n let buffer = "";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return JSON.parse(buffer);\n }\n case "text/html":\n case "text/plain": {\n let buffer = "";\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value);\n }\n return buffer;\n }\n default:\n return response.body;\n }\n}\n\nexport function chunked(response: Response) {\n return response.body;\n}\n\nexport async function buffered(response: Response) {\n const contentType = response.headers.get("Content-Type");\n if (!contentType) {\n throw new Error("Content-Type header is missing");\n }\n\n if (response.status === 204) {\n return null;\n }\n\n const { type } = parse(contentType);\n switch (type) {\n case "application/json":\n return response.json();\n case "text/plain":\n return response.text();\n case "text/html":\n return response.text();\n case "text/xml":\n case "application/xml":\n return response.text();\n case "application/x-www-form-urlencoded": {\n const text = await response.text();\n return Object.fromEntries(new URLSearchParams(text));\n }\n case "multipart/form-data":\n return response.formData();\n default:\n throw new Error(`Unsupported content type: ${contentType}`);\n }\n}\n';
1068
1110
 
1069
1111
  // packages/typescript/src/lib/http/parser.txt
1070
1112
  var parser_default = "import { z } from 'zod';\n\nexport type ParseError<T extends z.ZodType<any, any, any>> = {\n kind: 'parse';\n} & z.inferFlattenedErrors<T>;\n\nexport function parse<T extends z.ZodType>(\n schema: T,\n input: unknown,\n) {\n const result = schema.safeParse(input);\n if (!result.success) {\n const errors = result.error.flatten((issue) => issue);\n return [null, errors];\n }\n return [result.data as z.infer<T>, null];\n}\n";
@@ -1076,7 +1118,7 @@ var request_default = "export type Method = 'GET' | 'POST' | 'PUT' | 'PATCH' | '
1076
1118
  var response_default = "export interface ApiResponse<Status extends number, Body extends unknown> {\n kind: 'response';\n status: Status;\n body: Body;\n}\n\n// 4xx Client Errors\nexport type BadRequest = ApiResponse<400, { message: string }>;\nexport type Unauthorized = ApiResponse<401, { message: string }>;\nexport type PaymentRequired = ApiResponse<402, { message: string }>;\nexport type Forbidden = ApiResponse<403, { message: string }>;\nexport type NotFound = ApiResponse<404, { message: string }>;\nexport type MethodNotAllowed = ApiResponse<405, { message: string }>;\nexport type NotAcceptable = ApiResponse<406, { message: string }>;\nexport type Conflict = ApiResponse<409, { message: string }>;\nexport type Gone = ApiResponse<410, { message: string }>;\nexport type UnprocessableEntity = ApiResponse<422, { message: string; errors?: Record<string, string[]> }>;\nexport type TooManyRequests = ApiResponse<429, { message: string; retryAfter?: string }>;\nexport type PayloadTooLarge = ApiResponse<413, { message: string; }>;\nexport type UnsupportedMediaType = ApiResponse<415, { message: string; }>;\n\n// 5xx Server Errors\nexport type InternalServerError = ApiResponse<500, { message: string }>;\nexport type NotImplemented = ApiResponse<501, { message: string }>;\nexport type BadGateway = ApiResponse<502, { message: string }>;\nexport type ServiceUnavailable = ApiResponse<503, { message: string; retryAfter?: string }>;\nexport type GatewayTimeout = ApiResponse<504, { message: string }>;\n\nexport type ClientError =\n | BadRequest\n | Unauthorized\n | PaymentRequired\n | Forbidden\n | NotFound\n | MethodNotAllowed\n | NotAcceptable\n | Conflict\n | Gone\n | UnprocessableEntity\n | TooManyRequests;\n\nexport type ServerError =\n | InternalServerError\n | NotImplemented\n | BadGateway\n | ServiceUnavailable\n | GatewayTimeout;\n\nexport type ProblematicResponse = ClientError | ServerError;\n";
1077
1119
 
1078
1120
  // packages/typescript/src/lib/http/send-request.txt
1079
- var send_request_default = "import z from 'zod';\n\nimport type { Interceptor } from './interceptors.ts';\nimport { handleError, parseResponse } from './parse-response.ts';\nimport { parse } from './parser.ts';\n\nexport interface RequestSchema {\n schema: z.ZodType;\n toRequest: (input: any) => Request;\n}\n\nexport const fetchType = z\n .function()\n .args(z.instanceof(Request))\n .returns(z.promise(z.instanceof(Response)))\n .optional();\n\nexport async function sendRequest(\n input: any,\n route: RequestSchema,\n options: {\n fetch?: z.infer<typeof fetchType>;\n interceptors?: Interceptor[];\n },\n) {\n const { interceptors = [] } = options;\n const [parsedInput, parseError] = parse(route.schema, input);\n if (parseError) {\n return [null as never, { ...parseError, kind: 'parse' } as never] as const;\n }\n\n let request = route.toRequest(parsedInput as never);\n for (const interceptor of interceptors) {\n if (interceptor.before) {\n request = await interceptor.before(request);\n }\n }\n\n let response = await (options.fetch ?? fetch)(request);\n\n for (let i = interceptors.length - 1; i >= 0; i--) {\n const interceptor = interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n\n if (response.ok) {\n const data = await parseResponse(response);\n return [data as never, null] as const;\n }\n const error = await handleError(response);\n return [null as never, { ...error, kind: 'response' }] as const;\n}\n";
1121
+ var send_request_default = "import z from 'zod';\n\nimport type { Interceptor } from './interceptors.ts';\nimport { handleError } from './parse-response.ts';\nimport { parse } from './parser.ts';\n\nexport interface RequestSchema {\n schema: z.ZodType;\n toRequest: (input: unknown) => Request;\n deserializer: (response: Response) => Promise<unknown> | unknown;\n}\n\nexport const fetchType = z\n .function()\n .args(z.instanceof(Request))\n .returns(z.promise(z.instanceof(Response)))\n .optional();\n\nexport async function sendRequest(\n input: unknown,\n route: RequestSchema,\n options: {\n fetch?: z.infer<typeof fetchType>;\n interceptors?: Interceptor[];\n },\n) {\n const { interceptors = [] } = options;\n const [parsedInput, parseError] = parse(route.schema, input);\n if (parseError) {\n return [null as never, { ...parseError, kind: 'parse' } as never] as const;\n }\n\n let request = route.toRequest(parsedInput as never);\n for (const interceptor of interceptors) {\n if (interceptor.before) {\n request = await interceptor.before(request);\n }\n }\n\n let response = await (options.fetch ?? fetch)(request);\n\n for (let i = interceptors.length - 1; i >= 0; i--) {\n const interceptor = interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n\n if (response.ok) {\n const data = await route.deserializer(response);\n return [data as never, null] as const;\n }\n const error = await handleError(response);\n return [null as never, { ...error, kind: 'response' }] as const;\n}\n";
1080
1122
 
1081
1123
  // packages/typescript/src/lib/generate.ts
1082
1124
  function security(spec) {
@@ -1138,7 +1180,7 @@ async function generate(spec, settings) {
1138
1180
  `models/${name}.ts`,
1139
1181
  [
1140
1182
  `import { z } from 'zod';`,
1141
- ...exclude2(modelsImports, [name]).map(
1183
+ ...exclude(modelsImports, [name]).map(
1142
1184
  (it) => `import type { ${it} } from './${it}.ts';`
1143
1185
  ),
1144
1186
  `export type ${name} = ${schema};`
@@ -1173,6 +1215,7 @@ async function generate(spec, settings) {
1173
1215
  ignoreIfExists: true,
1174
1216
  content: JSON.stringify(
1175
1217
  {
1218
+ name: "sdk",
1176
1219
  type: "module",
1177
1220
  main: "./src/index.ts",
1178
1221
  dependencies: {
@@ -1185,7 +1228,7 @@ async function generate(spec, settings) {
1185
1228
  )
1186
1229
  },
1187
1230
  "tsconfig.json": {
1188
- ignoreIfExists: false,
1231
+ ignoreIfExists: true,
1189
1232
  content: JSON.stringify(
1190
1233
  {
1191
1234
  compilerOptions: {