@sdk-it/spec 0.23.0 → 0.25.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.
@@ -1,3 +1,3 @@
1
- import type { OurOpenAPIObject } from './types.ts';
1
+ import type { OurOpenAPIObject } from './types.js';
2
2
  export declare function getRefUsage(spec: OurOpenAPIObject, schemaName: string, list?: string[]): string[];
3
3
  //# sourceMappingURL=get-ref-usage.d.ts.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/get-ref-usage.ts"],
4
- "sourcesContent": ["import type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31';\n\nimport { isEmpty, isRef, parseRef, pascalcase } from '@sdk-it/core';\n\nimport type { OurOpenAPIObject } from './types.ts';\n\nexport function getRefUsage(\n spec: OurOpenAPIObject,\n schemaName: string,\n list: string[] = [],\n): string[] {\n const checkSchema = (\n schema: SchemaObject | ReferenceObject,\n withRefCheck = false,\n ): any => {\n if (isRef(schema)) {\n if (!withRefCheck) return false;\n const { model } = parseRef(schema.$ref);\n return model === schemaName;\n }\n if (!isEmpty(schema.oneOf)) {\n return schema.oneOf.some((it) => checkSchema(it, true));\n }\n if (schema.type === 'array' && schema.items) {\n if (isRef(schema.items)) {\n return checkSchema(schema.items, withRefCheck);\n }\n if (schema.items.oneOf) {\n return schema.items.oneOf.some((it) => checkSchema(it, true));\n }\n return checkSchema(schema.items, withRefCheck);\n }\n\n if (schema.type === 'object') {\n const properties = schema.properties;\n if (!isEmpty(properties)) {\n let found = false;\n let propertyName = '';\n for (const [key, it] of Object.entries(properties)) {\n found = checkSchema(it, false);\n if (found) {\n propertyName = key;\n }\n }\n return propertyName;\n }\n }\n return false;\n };\n\n for (const [key, value] of Object.entries(spec.components.schemas)) {\n const thisWouldBeTheObjectPropertyKeyName = checkSchema(value);\n if (thisWouldBeTheObjectPropertyKeyName) {\n if (typeof thisWouldBeTheObjectPropertyKeyName === 'string') {\n list.push(pascalcase(`${key} ${thisWouldBeTheObjectPropertyKeyName}`));\n } else {\n list.push(pascalcase(key));\n }\n }\n }\n\n return list;\n}\n"],
4
+ "sourcesContent": ["import type { ReferenceObject, SchemaObject } from 'openapi3-ts/oas31';\n\nimport { isEmpty, isRef, parseRef, pascalcase } from '@sdk-it/core';\n\nimport type { OurOpenAPIObject } from './types.js';\n\nexport function getRefUsage(\n spec: OurOpenAPIObject,\n schemaName: string,\n list: string[] = [],\n): string[] {\n const checkSchema = (\n schema: SchemaObject | ReferenceObject,\n withRefCheck = false,\n ): any => {\n if (isRef(schema)) {\n if (!withRefCheck) return false;\n const { model } = parseRef(schema.$ref);\n return model === schemaName;\n }\n if (!isEmpty(schema.oneOf)) {\n return schema.oneOf.some((it) => checkSchema(it, true));\n }\n if (schema.type === 'array' && schema.items) {\n if (isRef(schema.items)) {\n return checkSchema(schema.items, withRefCheck);\n }\n if (schema.items.oneOf) {\n return schema.items.oneOf.some((it) => checkSchema(it, true));\n }\n return checkSchema(schema.items, withRefCheck);\n }\n\n if (schema.type === 'object') {\n const properties = schema.properties;\n if (!isEmpty(properties)) {\n let found = false;\n let propertyName = '';\n for (const [key, it] of Object.entries(properties)) {\n found = checkSchema(it, false);\n if (found) {\n propertyName = key;\n }\n }\n return propertyName;\n }\n }\n return false;\n };\n\n for (const [key, value] of Object.entries(spec.components.schemas)) {\n const thisWouldBeTheObjectPropertyKeyName = checkSchema(value);\n if (thisWouldBeTheObjectPropertyKeyName) {\n if (typeof thisWouldBeTheObjectPropertyKeyName === 'string') {\n list.push(pascalcase(`${key} ${thisWouldBeTheObjectPropertyKeyName}`));\n } else {\n list.push(pascalcase(key));\n }\n }\n }\n\n return list;\n}\n"],
5
5
  "mappings": "AAEA,SAAS,SAAS,OAAO,UAAU,kBAAkB;AAI9C,SAAS,YACd,MACA,YACA,OAAiB,CAAC,GACR;AACV,QAAM,cAAc,CAClB,QACA,eAAe,UACP;AACR,QAAI,MAAM,MAAM,GAAG;AACjB,UAAI,CAAC,aAAc,QAAO;AAC1B,YAAM,EAAE,MAAM,IAAI,SAAS,OAAO,IAAI;AACtC,aAAO,UAAU;AAAA,IACnB;AACA,QAAI,CAAC,QAAQ,OAAO,KAAK,GAAG;AAC1B,aAAO,OAAO,MAAM,KAAK,CAAC,OAAO,YAAY,IAAI,IAAI,CAAC;AAAA,IACxD;AACA,QAAI,OAAO,SAAS,WAAW,OAAO,OAAO;AAC3C,UAAI,MAAM,OAAO,KAAK,GAAG;AACvB,eAAO,YAAY,OAAO,OAAO,YAAY;AAAA,MAC/C;AACA,UAAI,OAAO,MAAM,OAAO;AACtB,eAAO,OAAO,MAAM,MAAM,KAAK,CAAC,OAAO,YAAY,IAAI,IAAI,CAAC;AAAA,MAC9D;AACA,aAAO,YAAY,OAAO,OAAO,YAAY;AAAA,IAC/C;AAEA,QAAI,OAAO,SAAS,UAAU;AAC5B,YAAM,aAAa,OAAO;AAC1B,UAAI,CAAC,QAAQ,UAAU,GAAG;AACxB,YAAI,QAAQ;AACZ,YAAI,eAAe;AACnB,mBAAW,CAAC,KAAK,EAAE,KAAK,OAAO,QAAQ,UAAU,GAAG;AAClD,kBAAQ,YAAY,IAAI,KAAK;AAC7B,cAAI,OAAO;AACT,2BAAe;AAAA,UACjB;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,WAAW,OAAO,GAAG;AAClE,UAAM,sCAAsC,YAAY,KAAK;AAC7D,QAAI,qCAAqC;AACvC,UAAI,OAAO,wCAAwC,UAAU;AAC3D,aAAK,KAAK,WAAW,GAAG,GAAG,IAAI,mCAAmC,EAAE,CAAC;AAAA,MACvE,OAAO;AACL,aAAK,KAAK,WAAW,GAAG,CAAC;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  export declare function readWriteJson<T = Record<string, unknown>>(path: string): Promise<{
2
2
  content: Partial<T>;
3
- write: (value?: Record<string, any>) => Promise<void>;
3
+ write: (value?: Record<string, unknown>) => Promise<void>;
4
4
  }>;
5
5
  interface Metadata {
6
6
  generatedFiles: string[];
@@ -8,7 +8,7 @@ interface Metadata {
8
8
  }
9
9
  export declare function readWriteMetadata(output: string, files: string[]): Promise<{
10
10
  content: Partial<Metadata>;
11
- write: (value?: Record<string, any>) => Promise<void>;
11
+ write: (value?: Record<string, unknown>) => Promise<void>;
12
12
  }>;
13
13
  /**
14
14
  * Must run this file before writing the index files
@@ -1 +1 @@
1
- {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/lib/metadata.ts"],"names":[],"mappings":"AAcA,wBAAsB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM;;oBAM1D,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;GAGrC;AAED,UAAU,QAAQ;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;oBAVpD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;GAgBrC;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,EAC3B,MAAM,EAAE,MAAM,EACd,oBAAoB,GAAE,MAAM,EAAO,iBAgCpC"}
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/lib/metadata.ts"],"names":[],"mappings":"AAcA,wBAAsB,aAAa,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM;;oBAM1D,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;GAGzC;AAED,UAAU,QAAQ;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;oBAVpD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;GAgBzC;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,EAC3B,MAAM,EAAE,MAAM,EACd,oBAAoB,GAAE,MAAM,EAAO,iBA4CpC"}
@@ -29,16 +29,28 @@ async function cleanFiles(metadata, output, alwaysAvailableFiles = []) {
29
29
  const user = metadata.userFiles ?? [];
30
30
  const keep = [...generated, ...user, ...alwaysAvailableFiles];
31
31
  const actualFiles = (await readFolder(output, true)).map(addLeadingSlash);
32
- const filesToDelete = actualFiles.filter(
33
- (file) => !micromatch.isMatch(file, keep, { cwd: join(process.cwd(), output) })
34
- );
32
+ const filesToDelete = [
33
+ ...new Set(
34
+ actualFiles.filter(
35
+ (file) => !micromatch.isMatch(file, keep, { cwd: join(process.cwd(), output) })
36
+ )
37
+ )
38
+ ];
35
39
  const limit = pLimit(availableParallelism());
36
40
  await Promise.all(
37
41
  filesToDelete.map(
38
42
  (file) => limit(async () => {
39
43
  const filePath = join(output, file);
40
- await unlink(filePath);
41
- log(`Deleted file: ${filePath}`);
44
+ try {
45
+ await unlink(filePath);
46
+ log(`Deleted file: ${filePath}`);
47
+ } catch (error) {
48
+ if (error?.code === "ENOENT") {
49
+ log(`File already deleted or doesn't exist: ${filePath}`);
50
+ } else {
51
+ throw error;
52
+ }
53
+ }
42
54
  })
43
55
  )
44
56
  );
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/metadata.ts"],
4
- "sourcesContent": ["import deubg from 'debug';\nimport { readFile, unlink, writeFile } from 'node:fs/promises';\nimport { availableParallelism } from 'node:os';\nimport { join } from 'node:path';\nimport pLimit from 'p-limit';\n\nimport {\n addLeadingSlash,\n exist,\n readFolder,\n} from '@sdk-it/core/file-system.js';\n\nconst log = deubg('sdk-it:metdata');\n\nexport async function readWriteJson<T = Record<string, unknown>>(path: string) {\n const content = (\n (await exist(path)) ? JSON.parse(await readFile(path, 'utf-8')) : {}\n ) as Partial<T>;\n return {\n content,\n write: (value: Record<string, any> = content) =>\n writeFile(path, JSON.stringify(value, null, 2), 'utf-8'),\n };\n}\n\ninterface Metadata {\n generatedFiles: string[];\n userFiles?: string[];\n}\n\nexport async function readWriteMetadata(output: string, files: string[]) {\n const metadata = await readWriteJson<Metadata>(join(output, 'metadata.json'));\n metadata.content.generatedFiles = files;\n metadata.content.userFiles ??= ['/dist/**', '/build/**', '/readme.md'];\n await metadata.write(metadata.content);\n return metadata;\n}\n\n/**\n * Must run this file before writing the index files\n * otherwise the index.ts file will have have references to files\n * that are deleted (old, non used generated files)\n * @param metadata\n * @param output\n */\nexport async function cleanFiles(\n metadata: Partial<Metadata>,\n output: string,\n alwaysAvailableFiles: string[] = [],\n) {\n const { default: micromatch } = await import('micromatch');\n const generated = metadata.generatedFiles ?? [];\n const user = metadata.userFiles ?? [];\n const keep = [...generated, ...user, ...alwaysAvailableFiles];\n const actualFiles = (await readFolder(output, true)).map(addLeadingSlash);\n\n const filesToDelete = actualFiles.filter(\n (file) =>\n !micromatch.isMatch(file, keep, { cwd: join(process.cwd(), output) }),\n );\n const limit = pLimit(availableParallelism());\n\n await Promise.all(\n filesToDelete.map((file) =>\n limit(async () => {\n const filePath = join(output, file);\n await unlink(filePath);\n log(`Deleted file: ${filePath}`);\n }),\n ),\n );\n\n // for (const file of actualFiles) {\n // if (micromatch.isMatch(addLeadingSlash(file), keep)) {\n // continue;\n // }\n // const filePath = join(output, file);\n // await unlink(filePath);\n // log(`Deleted file: ${filePath}`);\n // }\n}\n"],
5
- "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,UAAU,QAAQ,iBAAiB;AAC5C,SAAS,4BAA4B;AACrC,SAAS,YAAY;AACrB,OAAO,YAAY;AAEnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,MAAM,MAAM,gBAAgB;AAElC,eAAsB,cAA2C,MAAc;AAC7E,QAAM,UACH,MAAM,MAAM,IAAI,IAAK,KAAK,MAAM,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;AAErE,SAAO;AAAA,IACL;AAAA,IACA,OAAO,CAAC,QAA6B,YACnC,UAAU,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAAA,EAC3D;AACF;AAOA,eAAsB,kBAAkB,QAAgB,OAAiB;AACvE,QAAM,WAAW,MAAM,cAAwB,KAAK,QAAQ,eAAe,CAAC;AAC5E,WAAS,QAAQ,iBAAiB;AAClC,WAAS,QAAQ,cAAc,CAAC,YAAY,aAAa,YAAY;AACrE,QAAM,SAAS,MAAM,SAAS,OAAO;AACrC,SAAO;AACT;AASA,eAAsB,WACpB,UACA,QACA,uBAAiC,CAAC,GAClC;AACA,QAAM,EAAE,SAAS,WAAW,IAAI,MAAM,OAAO,YAAY;AACzD,QAAM,YAAY,SAAS,kBAAkB,CAAC;AAC9C,QAAM,OAAO,SAAS,aAAa,CAAC;AACpC,QAAM,OAAO,CAAC,GAAG,WAAW,GAAG,MAAM,GAAG,oBAAoB;AAC5D,QAAM,eAAe,MAAM,WAAW,QAAQ,IAAI,GAAG,IAAI,eAAe;AAExE,QAAM,gBAAgB,YAAY;AAAA,IAChC,CAAC,SACC,CAAC,WAAW,QAAQ,MAAM,MAAM,EAAE,KAAK,KAAK,QAAQ,IAAI,GAAG,MAAM,EAAE,CAAC;AAAA,EACxE;AACA,QAAM,QAAQ,OAAO,qBAAqB,CAAC;AAE3C,QAAM,QAAQ;AAAA,IACZ,cAAc;AAAA,MAAI,CAAC,SACjB,MAAM,YAAY;AAChB,cAAM,WAAW,KAAK,QAAQ,IAAI;AAClC,cAAM,OAAO,QAAQ;AACrB,YAAI,iBAAiB,QAAQ,EAAE;AAAA,MACjC,CAAC;AAAA,IACH;AAAA,EACF;AAUF;",
4
+ "sourcesContent": ["import deubg from 'debug';\nimport { readFile, unlink, writeFile } from 'node:fs/promises';\nimport { availableParallelism } from 'node:os';\nimport { join } from 'node:path';\nimport pLimit from 'p-limit';\n\nimport {\n addLeadingSlash,\n exist,\n readFolder,\n} from '@sdk-it/core/file-system.js';\n\nconst log = deubg('sdk-it:metdata');\n\nexport async function readWriteJson<T = Record<string, unknown>>(path: string) {\n const content = (\n (await exist(path)) ? JSON.parse(await readFile(path, 'utf-8')) : {}\n ) as Partial<T>;\n return {\n content,\n write: (value: Record<string, unknown> = content) =>\n writeFile(path, JSON.stringify(value, null, 2), 'utf-8'),\n };\n}\n\ninterface Metadata {\n generatedFiles: string[];\n userFiles?: string[];\n}\n\nexport async function readWriteMetadata(output: string, files: string[]) {\n const metadata = await readWriteJson<Metadata>(join(output, 'metadata.json'));\n metadata.content.generatedFiles = files;\n metadata.content.userFiles ??= ['/dist/**', '/build/**', '/readme.md'];\n await metadata.write(metadata.content);\n return metadata;\n}\n\n/**\n * Must run this file before writing the index files\n * otherwise the index.ts file will have have references to files\n * that are deleted (old, non used generated files)\n * @param metadata\n * @param output\n */\nexport async function cleanFiles(\n metadata: Partial<Metadata>,\n output: string,\n alwaysAvailableFiles: string[] = [],\n) {\n const { default: micromatch } = await import('micromatch');\n const generated = metadata.generatedFiles ?? [];\n const user = metadata.userFiles ?? [];\n const keep = [...generated, ...user, ...alwaysAvailableFiles];\n const actualFiles = (await readFolder(output, true)).map(addLeadingSlash);\n const filesToDelete = [\n ...new Set(\n actualFiles.filter(\n (file) =>\n !micromatch.isMatch(file, keep, { cwd: join(process.cwd(), output) }),\n ),\n ),\n ];\n const limit = pLimit(availableParallelism());\n\n await Promise.all(\n filesToDelete.map((file) =>\n limit(async () => {\n const filePath = join(output, file);\n try {\n await unlink(filePath);\n log(`Deleted file: ${filePath}`);\n } catch (error: unknown) {\n // Ignore ENOENT errors - file was already deleted or doesn't exist\n if ((error as NodeJS.ErrnoException)?.code === 'ENOENT') {\n log(`File already deleted or doesn't exist: ${filePath}`);\n } else {\n throw error;\n }\n }\n }),\n ),\n );\n\n // for (const file of actualFiles) {\n // if (micromatch.isMatch(addLeadingSlash(file), keep)) {\n // continue;\n // }\n // const filePath = join(output, file);\n // await unlink(filePath);\n // log(`Deleted file: ${filePath}`);\n // }\n}\n"],
5
+ "mappings": "AAAA,OAAO,WAAW;AAClB,SAAS,UAAU,QAAQ,iBAAiB;AAC5C,SAAS,4BAA4B;AACrC,SAAS,YAAY;AACrB,OAAO,YAAY;AAEnB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,MAAM,MAAM,MAAM,gBAAgB;AAElC,eAAsB,cAA2C,MAAc;AAC7E,QAAM,UACH,MAAM,MAAM,IAAI,IAAK,KAAK,MAAM,MAAM,SAAS,MAAM,OAAO,CAAC,IAAI,CAAC;AAErE,SAAO;AAAA,IACL;AAAA,IACA,OAAO,CAAC,QAAiC,YACvC,UAAU,MAAM,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;AAAA,EAC3D;AACF;AAOA,eAAsB,kBAAkB,QAAgB,OAAiB;AACvE,QAAM,WAAW,MAAM,cAAwB,KAAK,QAAQ,eAAe,CAAC;AAC5E,WAAS,QAAQ,iBAAiB;AAClC,WAAS,QAAQ,cAAc,CAAC,YAAY,aAAa,YAAY;AACrE,QAAM,SAAS,MAAM,SAAS,OAAO;AACrC,SAAO;AACT;AASA,eAAsB,WACpB,UACA,QACA,uBAAiC,CAAC,GAClC;AACA,QAAM,EAAE,SAAS,WAAW,IAAI,MAAM,OAAO,YAAY;AACzD,QAAM,YAAY,SAAS,kBAAkB,CAAC;AAC9C,QAAM,OAAO,SAAS,aAAa,CAAC;AACpC,QAAM,OAAO,CAAC,GAAG,WAAW,GAAG,MAAM,GAAG,oBAAoB;AAC5D,QAAM,eAAe,MAAM,WAAW,QAAQ,IAAI,GAAG,IAAI,eAAe;AACxE,QAAM,gBAAgB;AAAA,IACpB,GAAG,IAAI;AAAA,MACL,YAAY;AAAA,QACV,CAAC,SACC,CAAC,WAAW,QAAQ,MAAM,MAAM,EAAE,KAAK,KAAK,QAAQ,IAAI,GAAG,MAAM,EAAE,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACA,QAAM,QAAQ,OAAO,qBAAqB,CAAC;AAE3C,QAAM,QAAQ;AAAA,IACZ,cAAc;AAAA,MAAI,CAAC,SACjB,MAAM,YAAY;AAChB,cAAM,WAAW,KAAK,QAAQ,IAAI;AAClC,YAAI;AACF,gBAAM,OAAO,QAAQ;AACrB,cAAI,iBAAiB,QAAQ,EAAE;AAAA,QACjC,SAAS,OAAgB;AAEvB,cAAK,OAAiC,SAAS,UAAU;AACvD,gBAAI,0CAA0C,QAAQ,EAAE;AAAA,UAC1D,OAAO;AACL,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAUF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=metadata.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.test.d.ts","sourceRoot":"","sources":["../../src/lib/metadata.test.ts"],"names":[],"mappings":""}
@@ -1,5 +1,5 @@
1
1
  import type { ParameterLocation, ParameterObject, ReferenceObject, SchemaObject, SecurityRequirementObject, SecuritySchemeObject } from 'openapi3-ts/oas31';
2
- import type { OurOpenAPIObject } from './types.ts';
2
+ import type { OurOpenAPIObject } from './types.js';
3
3
  type OIn = ParameterLocation | 'input';
4
4
  export type OurParameter = Omit<ParameterObject, 'in' | 'schema'> & {
5
5
  in: OIn;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/security.ts"],
4
- "sourcesContent": ["import type {\n ParameterLocation,\n ParameterObject,\n ReferenceObject,\n SchemaObject,\n SecurityRequirementObject,\n SecuritySchemeObject,\n} from 'openapi3-ts/oas31';\n\nimport { methods } from '@sdk-it/core/paths.js';\nimport { resolveRef } from '@sdk-it/core/ref.js';\n\nimport type { OurOpenAPIObject } from './types.ts';\n\ntype OIn = ParameterLocation | 'input';\n\nexport type OurParameter = Omit<ParameterObject, 'in' | 'schema'> & {\n in: OIn;\n 'x-optionName'?: string;\n schema: SchemaObject;\n};\n\nexport function securityToOptions(\n spec: OurOpenAPIObject,\n security: SecurityRequirementObject[],\n securitySchemes: Record<string, SecuritySchemeObject | ReferenceObject>,\n staticIn?: OIn,\n) {\n const parameters: OurParameter[] = [];\n for (const it of security) {\n const [name] = Object.keys(it);\n if (!name) {\n // this means the operation doesn't necessarily require security\n continue;\n }\n\n const schema = resolveRef<SecuritySchemeObject>(\n spec,\n securitySchemes[name],\n );\n if (schema.type === 'http') {\n parameters.push({\n in: staticIn ?? 'header',\n name: 'authorization',\n required: false,\n schema: { type: 'string', 'x-prefix': 'Bearer ' },\n 'x-optionName': 'token',\n example:\n schema.scheme === 'bearer'\n ? '\"<token>\"'\n : `<${schema.scheme}> <token>`,\n });\n continue;\n }\n if (schema.type === 'apiKey') {\n if (!schema.in) {\n throw new Error(`apiKey security schema must have an \"in\" field`);\n }\n if (!schema.name) {\n throw new Error(`apiKey security schema must have a \"name\" field`);\n }\n parameters.push({\n in: staticIn ?? (schema.in as ParameterLocation),\n name: schema.name,\n required: false,\n schema: { type: 'string' },\n example: `\"proj-${crypto.randomUUID().slice(0, 12)}\"`,\n });\n continue;\n }\n }\n return parameters;\n}\n\nexport function security(spec: OurOpenAPIObject) {\n const security = spec.security || [];\n const paths = Object.values(spec.paths ?? {});\n\n const options = securityToOptions(\n spec,\n security,\n spec.components.securitySchemes,\n );\n\n for (const it of paths) {\n for (const method of methods) {\n const operation = it[method];\n if (!operation) {\n continue;\n }\n Object.assign(\n options,\n securityToOptions(\n spec,\n operation.security || [],\n spec.components.securitySchemes,\n 'input',\n ),\n );\n }\n }\n return options;\n}\n"],
4
+ "sourcesContent": ["import type {\n ParameterLocation,\n ParameterObject,\n ReferenceObject,\n SchemaObject,\n SecurityRequirementObject,\n SecuritySchemeObject,\n} from 'openapi3-ts/oas31';\n\nimport { methods } from '@sdk-it/core/paths.js';\nimport { resolveRef } from '@sdk-it/core/ref.js';\n\nimport type { OurOpenAPIObject } from './types.js';\n\ntype OIn = ParameterLocation | 'input';\n\nexport type OurParameter = Omit<ParameterObject, 'in' | 'schema'> & {\n in: OIn;\n 'x-optionName'?: string;\n schema: SchemaObject;\n};\n\nexport function securityToOptions(\n spec: OurOpenAPIObject,\n security: SecurityRequirementObject[],\n securitySchemes: Record<string, SecuritySchemeObject | ReferenceObject>,\n staticIn?: OIn,\n) {\n const parameters: OurParameter[] = [];\n for (const it of security) {\n const [name] = Object.keys(it);\n if (!name) {\n // this means the operation doesn't necessarily require security\n continue;\n }\n\n const schema = resolveRef<SecuritySchemeObject>(\n spec,\n securitySchemes[name],\n );\n if (schema.type === 'http') {\n parameters.push({\n in: staticIn ?? 'header',\n name: 'authorization',\n required: false,\n schema: { type: 'string', 'x-prefix': 'Bearer ' },\n 'x-optionName': 'token',\n example:\n schema.scheme === 'bearer'\n ? '\"<token>\"'\n : `<${schema.scheme}> <token>`,\n });\n continue;\n }\n if (schema.type === 'apiKey') {\n if (!schema.in) {\n throw new Error(`apiKey security schema must have an \"in\" field`);\n }\n if (!schema.name) {\n throw new Error(`apiKey security schema must have a \"name\" field`);\n }\n parameters.push({\n in: staticIn ?? (schema.in as ParameterLocation),\n name: schema.name,\n required: false,\n schema: { type: 'string' },\n example: `\"proj-${crypto.randomUUID().slice(0, 12)}\"`,\n });\n continue;\n }\n }\n return parameters;\n}\n\nexport function security(spec: OurOpenAPIObject) {\n const security = spec.security || [];\n const paths = Object.values(spec.paths ?? {});\n\n const options = securityToOptions(\n spec,\n security,\n spec.components.securitySchemes,\n );\n\n for (const it of paths) {\n for (const method of methods) {\n const operation = it[method];\n if (!operation) {\n continue;\n }\n Object.assign(\n options,\n securityToOptions(\n spec,\n operation.security || [],\n spec.components.securitySchemes,\n 'input',\n ),\n );\n }\n }\n return options;\n}\n"],
5
5
  "mappings": "AASA,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAYpB,SAAS,kBACd,MACAA,WACA,iBACA,UACA;AACA,QAAM,aAA6B,CAAC;AACpC,aAAW,MAAMA,WAAU;AACzB,UAAM,CAAC,IAAI,IAAI,OAAO,KAAK,EAAE;AAC7B,QAAI,CAAC,MAAM;AAET;AAAA,IACF;AAEA,UAAM,SAAS;AAAA,MACb;AAAA,MACA,gBAAgB,IAAI;AAAA,IACtB;AACA,QAAI,OAAO,SAAS,QAAQ;AAC1B,iBAAW,KAAK;AAAA,QACd,IAAI,YAAY;AAAA,QAChB,MAAM;AAAA,QACN,UAAU;AAAA,QACV,QAAQ,EAAE,MAAM,UAAU,YAAY,UAAU;AAAA,QAChD,gBAAgB;AAAA,QAChB,SACE,OAAO,WAAW,WACd,cACA,IAAI,OAAO,MAAM;AAAA,MACzB,CAAC;AACD;AAAA,IACF;AACA,QAAI,OAAO,SAAS,UAAU;AAC5B,UAAI,CAAC,OAAO,IAAI;AACd,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AACA,UAAI,CAAC,OAAO,MAAM;AAChB,cAAM,IAAI,MAAM,iDAAiD;AAAA,MACnE;AACA,iBAAW,KAAK;AAAA,QACd,IAAI,YAAa,OAAO;AAAA,QACxB,MAAM,OAAO;AAAA,QACb,UAAU;AAAA,QACV,QAAQ,EAAE,MAAM,SAAS;AAAA,QACzB,SAAS,SAAS,OAAO,WAAW,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,MACpD,CAAC;AACD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,SAAS,MAAwB;AAC/C,QAAMA,YAAW,KAAK,YAAY,CAAC;AACnC,QAAM,QAAQ,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC;AAE5C,QAAM,UAAU;AAAA,IACd;AAAA,IACAA;AAAA,IACA,KAAK,WAAW;AAAA,EAClB;AAEA,aAAW,MAAM,OAAO;AACtB,eAAW,UAAU,SAAS;AAC5B,YAAM,YAAY,GAAG,MAAM;AAC3B,UAAI,CAAC,WAAW;AACd;AAAA,MACF;AACA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE;AAAA,UACA,UAAU,YAAY,CAAC;AAAA,UACvB,KAAK,WAAW;AAAA,UAChB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;",
6
6
  "names": ["security"]
7
7
  }
@@ -1,4 +1,4 @@
1
- import type { OurOpenAPIObject } from './types.ts';
1
+ import type { OurOpenAPIObject } from './types.js';
2
2
  export type ChildNavItem = {
3
3
  id: string;
4
4
  title: string;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/sidebar.ts"],
4
- "sourcesContent": ["import type { OpenAPIObject } from 'openapi3-ts/oas31';\nimport { camelcase } from 'stringcase';\n\nimport { forEachOperation } from './for-each-operation.js';\nimport type { OurOpenAPIObject, TunedOperationObject } from './types.ts';\n\nexport type ChildNavItem = {\n id: string;\n title: string;\n url: string;\n};\n\nexport type NavItem = {\n id: string;\n title: string;\n description?: string;\n url?: string;\n items?: ChildNavItem[];\n content?: string; // Markdown content\n};\n\nexport type CategoryItem = {\n id: string;\n category: string;\n description?: string;\n items: NavItem[];\n};\n\nexport type SidebarData = CategoryItem[];\n\nfunction createOAIMeta(spec: OurOpenAPIObject): XOaiMeta {\n spec.paths ??= {};\n const navigationGroups: Record<string, NavigationGroup> = {\n api: { id: 'api', title: 'API' },\n };\n const groups: Record<string, Group> = {};\n\n forEachOperation(spec, (entry, operation) => {\n const tag = entry.tag;\n groups[tag] ??= {\n id: tag,\n title: tag,\n description: spec.tags?.find((t) => t.name === tag)?.description,\n navigationGroup: 'api',\n sections: [],\n };\n groups[tag].sections.push({\n type: 'endpoint',\n key: operation.operationId,\n path: entry.path,\n });\n });\n\n return {\n groups: Object.values(groups),\n navigationGroups: Object.values(navigationGroups),\n };\n}\n\nfunction getOperationById(spec: OurOpenAPIObject, operationId: string) {\n let operation: TunedOperationObject | undefined;\n forEachOperation(spec, (entry, op) => {\n if (op.operationId === operationId) {\n operation = op;\n }\n });\n return operation;\n}\n\nexport function toSidebar(spec: OurOpenAPIObject) {\n const openapi: OpenAPIObject & { 'x-oaiMeta': XOaiMeta } = spec as any;\n const sidebar: SidebarData = spec['x-docs'];\n const oaiMeta = openapi['x-oaiMeta'] ?? createOAIMeta(spec);\n for (const navGroup of oaiMeta.navigationGroups) {\n const group = oaiMeta.groups.filter(\n (group) => group.navigationGroup === navGroup.id,\n );\n if (group.length === 0) continue;\n const groupItems: NavItem[] = [];\n for (const item of group) {\n const subitems: ChildNavItem[] = (item.sections ?? [])\n .filter((it) => it.type === 'endpoint')\n .map((section) => {\n const operation = getOperationById(spec, section.key);\n const title =\n operation?.['x-oaiMeta']?.name || operation?.summary || section.key;\n return {\n id: section.key,\n title,\n url: `${item.id}/${camelcase(section.key)}`,\n };\n });\n if (subitems.length === 0) continue;\n groupItems.push({\n id: item.id,\n title: item.title,\n // url: `/${item.id}`,\n description: item.description,\n items: subitems,\n });\n }\n if (groupItems.length === 0) continue;\n sidebar.push({\n id: navGroup.id,\n category: navGroup.title,\n items: groupItems,\n });\n }\n return sidebar;\n}\n\nexport interface XOaiMeta {\n navigationGroups: NavigationGroup[];\n groups: Group[];\n}\n\nexport interface Group {\n id: string;\n title: string;\n description?: string;\n navigationGroup: string;\n sections: Section[];\n beta?: boolean;\n legacy?: boolean;\n}\n\nexport interface Section {\n type: 'endpoint' | 'object' | 'overview';\n key: string;\n path: string;\n}\n\nexport interface NavigationGroup {\n id: string;\n title: string;\n beta?: boolean;\n}\n"],
4
+ "sourcesContent": ["import type { OpenAPIObject } from 'openapi3-ts/oas31';\nimport { camelcase } from 'stringcase';\n\nimport { forEachOperation } from './for-each-operation.js';\nimport type { OurOpenAPIObject, TunedOperationObject } from './types.js';\n\nexport type ChildNavItem = {\n id: string;\n title: string;\n url: string;\n};\n\nexport type NavItem = {\n id: string;\n title: string;\n description?: string;\n url?: string;\n items?: ChildNavItem[];\n content?: string; // Markdown content\n};\n\nexport type CategoryItem = {\n id: string;\n category: string;\n description?: string;\n items: NavItem[];\n};\n\nexport type SidebarData = CategoryItem[];\n\nfunction createOAIMeta(spec: OurOpenAPIObject): XOaiMeta {\n spec.paths ??= {};\n const navigationGroups: Record<string, NavigationGroup> = {\n api: { id: 'api', title: 'API' },\n };\n const groups: Record<string, Group> = {};\n\n forEachOperation(spec, (entry, operation) => {\n const tag = entry.tag;\n groups[tag] ??= {\n id: tag,\n title: tag,\n description: spec.tags?.find((t) => t.name === tag)?.description,\n navigationGroup: 'api',\n sections: [],\n };\n groups[tag].sections.push({\n type: 'endpoint',\n key: operation.operationId,\n path: entry.path,\n });\n });\n\n return {\n groups: Object.values(groups),\n navigationGroups: Object.values(navigationGroups),\n };\n}\n\nfunction getOperationById(spec: OurOpenAPIObject, operationId: string) {\n let operation: TunedOperationObject | undefined;\n forEachOperation(spec, (entry, op) => {\n if (op.operationId === operationId) {\n operation = op;\n }\n });\n return operation;\n}\n\nexport function toSidebar(spec: OurOpenAPIObject) {\n const openapi: OpenAPIObject & { 'x-oaiMeta': XOaiMeta } = spec as any;\n const sidebar: SidebarData = spec['x-docs'];\n const oaiMeta = openapi['x-oaiMeta'] ?? createOAIMeta(spec);\n for (const navGroup of oaiMeta.navigationGroups) {\n const group = oaiMeta.groups.filter(\n (group) => group.navigationGroup === navGroup.id,\n );\n if (group.length === 0) continue;\n const groupItems: NavItem[] = [];\n for (const item of group) {\n const subitems: ChildNavItem[] = (item.sections ?? [])\n .filter((it) => it.type === 'endpoint')\n .map((section) => {\n const operation = getOperationById(spec, section.key);\n const title =\n operation?.['x-oaiMeta']?.name || operation?.summary || section.key;\n return {\n id: section.key,\n title,\n url: `${item.id}/${camelcase(section.key)}`,\n };\n });\n if (subitems.length === 0) continue;\n groupItems.push({\n id: item.id,\n title: item.title,\n // url: `/${item.id}`,\n description: item.description,\n items: subitems,\n });\n }\n if (groupItems.length === 0) continue;\n sidebar.push({\n id: navGroup.id,\n category: navGroup.title,\n items: groupItems,\n });\n }\n return sidebar;\n}\n\nexport interface XOaiMeta {\n navigationGroups: NavigationGroup[];\n groups: Group[];\n}\n\nexport interface Group {\n id: string;\n title: string;\n description?: string;\n navigationGroup: string;\n sections: Section[];\n beta?: boolean;\n legacy?: boolean;\n}\n\nexport interface Section {\n type: 'endpoint' | 'object' | 'overview';\n key: string;\n path: string;\n}\n\nexport interface NavigationGroup {\n id: string;\n title: string;\n beta?: boolean;\n}\n"],
5
5
  "mappings": "AACA,SAAS,iBAAiB;AAE1B,SAAS,wBAAwB;AA2BjC,SAAS,cAAc,MAAkC;AACvD,OAAK,UAAU,CAAC;AAChB,QAAM,mBAAoD;AAAA,IACxD,KAAK,EAAE,IAAI,OAAO,OAAO,MAAM;AAAA,EACjC;AACA,QAAM,SAAgC,CAAC;AAEvC,mBAAiB,MAAM,CAAC,OAAO,cAAc;AAC3C,UAAM,MAAM,MAAM;AAClB,WAAO,GAAG,MAAM;AAAA,MACd,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,aAAa,KAAK,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,GAAG;AAAA,MACrD,iBAAiB;AAAA,MACjB,UAAU,CAAC;AAAA,IACb;AACA,WAAO,GAAG,EAAE,SAAS,KAAK;AAAA,MACxB,MAAM;AAAA,MACN,KAAK,UAAU;AAAA,MACf,MAAM,MAAM;AAAA,IACd,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AAAA,IACL,QAAQ,OAAO,OAAO,MAAM;AAAA,IAC5B,kBAAkB,OAAO,OAAO,gBAAgB;AAAA,EAClD;AACF;AAEA,SAAS,iBAAiB,MAAwB,aAAqB;AACrE,MAAI;AACJ,mBAAiB,MAAM,CAAC,OAAO,OAAO;AACpC,QAAI,GAAG,gBAAgB,aAAa;AAClC,kBAAY;AAAA,IACd;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEO,SAAS,UAAU,MAAwB;AAChD,QAAM,UAAqD;AAC3D,QAAM,UAAuB,KAAK,QAAQ;AAC1C,QAAM,UAAU,QAAQ,WAAW,KAAK,cAAc,IAAI;AAC1D,aAAW,YAAY,QAAQ,kBAAkB;AAC/C,UAAM,QAAQ,QAAQ,OAAO;AAAA,MAC3B,CAACA,WAAUA,OAAM,oBAAoB,SAAS;AAAA,IAChD;AACA,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,aAAwB,CAAC;AAC/B,eAAW,QAAQ,OAAO;AACxB,YAAM,YAA4B,KAAK,YAAY,CAAC,GACjD,OAAO,CAAC,OAAO,GAAG,SAAS,UAAU,EACrC,IAAI,CAAC,YAAY;AAChB,cAAM,YAAY,iBAAiB,MAAM,QAAQ,GAAG;AACpD,cAAM,QACJ,YAAY,WAAW,GAAG,QAAQ,WAAW,WAAW,QAAQ;AAClE,eAAO;AAAA,UACL,IAAI,QAAQ;AAAA,UACZ;AAAA,UACA,KAAK,GAAG,KAAK,EAAE,IAAI,UAAU,QAAQ,GAAG,CAAC;AAAA,QAC3C;AAAA,MACF,CAAC;AACH,UAAI,SAAS,WAAW,EAAG;AAC3B,iBAAW,KAAK;AAAA,QACd,IAAI,KAAK;AAAA,QACT,OAAO,KAAK;AAAA;AAAA,QAEZ,aAAa,KAAK;AAAA,QAClB,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,QAAI,WAAW,WAAW,EAAG;AAC7B,YAAQ,KAAK;AAAA,MACX,IAAI,SAAS;AAAA,MACb,UAAU,SAAS;AAAA,MACnB,OAAO;AAAA,IACT,CAAC;AAAA,EACH;AACA,SAAO;AACT;",
6
6
  "names": ["group"]
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import type { ComponentsObject, MediaTypeObject, OpenAPIObject, OperationObject, ParameterObject, PathsObject, ReferenceObject, RequestBodyObject, ResponseObject, SchemaObject, SecuritySchemeObject } from 'openapi3-ts/oas31';
2
- import type { SidebarData } from './sidebar.ts';
2
+ import type { SidebarData } from './sidebar.js';
3
3
  export interface OurOpenAPIObject extends OpenAPIObject {
4
4
  'x-sdk-augmented'?: boolean;
5
5
  'x-docs': SidebarData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdk-it/spec",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -26,7 +26,7 @@
26
26
  "!**/*.tsbuildinfo"
27
27
  ],
28
28
  "dependencies": {
29
- "@sdk-it/core": "0.23.0",
29
+ "@sdk-it/core": "0.25.0",
30
30
  "openapi3-ts": "4.4.0",
31
31
  "pluralize": "^8.0.0",
32
32
  "stringcase": "^4.3.1",