Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

conjure-lite

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

conjure-lite - npm Package Compare versions

Comparing version 0.0.1 to 0.0.2

.eslintrc.cjs

26

package.json
{
"name": "conjure-lite",
"version": "0.0.1",
"version": "0.0.2",
"exports": {

@@ -11,18 +11,20 @@ ".": {

"bin": "./bin/conjure-lite.mjs",
"type": "module",
"scripts": {
"prepublish": "tsc"
},
"devDependencies": {
"@types/node": "^20.9.0",
"@types/yargs": "^17.0.31",
"bun-types": "latest",
"dprint": "^0.42.5"
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"conjure-api": "^4.31.0",
"dprint": "^0.42.5",
"eslint": "^8.53.0",
"typescript": "^5.2.2"
},
"peerDependencies": {
"typescript": "^5.0.0",
"tsup": "^7.2.0"
},
"dependencies": {
"conjure-api": "^4.31.0",
"prettier": "^3.0.3",
"yargs": "^17.7.2",
"@types/node": "^20.9.0",
"conjure-lite": "."
"dedent": "^1.5.1",
"yargs": "^17.7.2"
}
}

@@ -1,4 +0,6 @@

import yargs, { CommandModule } from "yargs";
import type { CommandModule } from "yargs";
import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { HandleGenerateArgs } from "./cli/HandleGenerateArgs.js";
import type { HandleGenerateArgs } from "./cli/HandleGenerateArgs.js";
import type {} from "node:process";

@@ -29,4 +31,3 @@ export async function cli() {

await yargs(hideBin(process.argv))
.command(generate).demandCommand().parseAsync();
}

@@ -1,67 +0,13 @@

import * as ConjureApi from "conjure-api";
import type * as ConjureApi from "conjure-api";
import * as fs from "node:fs";
import * as path from "node:path";
import { CodeGen } from "../codegen/CodeGen.js";
import { EndpointCodeFile } from "../codegen/EndpointCodeFile.js";
import { EnumCodeFile } from "../codegen/EnumCodeFile.js";
import { TypeAliasCodeFile } from "../codegen/TypeAliasCodeFile.js";
import { UnionCodeFile } from "../codegen/UnionCodeFile.js";
import { formatTs, writeCodeFile } from "../util/writeCodeFile.js";
import { HandleGenerateArgs } from "./HandleGenerateArgs.js";
import type { HandleGenerateArgs } from "./HandleGenerateArgs.js";
export async function handleGenerate(args: HandleGenerateArgs) {
const ir: ConjureApi.IConjureDefinition = JSON.parse(
await fs.promises.readFile(args.ir, "utf-8"),
await fs.promises.readFile(args.ir, "utf-8")
);
const codeGen = new CodeGen(ir, args);
for (const type of ir.types) {
if (type.type === "object") {
await writeCodeFile(
codeGen.getFilePath(type.object.typeName),
await formatTs(`export interface ${type.object.typeName.name} {}`),
);
} else if (type.type === "alias") {
new TypeAliasCodeFile(
codeGen.getFilePath(type.alias.typeName),
codeGen,
type.alias.typeName.name,
type.alias.alias,
).generate();
} else if (type.type === "enum") {
new EnumCodeFile(
codeGen.getFilePath(type.enum.typeName),
codeGen,
type,
).generate();
} else if (type.type === "union") {
new UnionCodeFile(
codeGen.getFilePath(type.union.typeName),
codeGen,
type,
).generate();
} else {
throw new Error(`Not implemented: ${(type as any).type}`);
}
}
for (const service of ir.services) {
const serviceDir = path.join(codeGen.getPathDir(service.serviceName), service.serviceName.name);
await fs.promises.mkdir(serviceDir, {
recursive: true,
});
for (const endpoint of service.endpoints) {
const endpointFilePath = `${path.join(serviceDir, endpoint.endpointName)}.ts`;
const endpointCodeFile = new EndpointCodeFile(endpointFilePath, codeGen, service, endpoint);
endpointCodeFile.generate();
}
await fs.promises.writeFile(
`${args.outDir}/${service.serviceName.name}.ts`,
`export interface ${service.serviceName.name} {}`,
"utf-8",
);
}
await codeGen.generate();
}

@@ -1,10 +0,12 @@

import * as ConjureApi from "conjure-api";
import type * as ConjureApi from "conjure-api";
import * as fs from "node:fs";
import * as path from "node:path";
import { writeCodeFile } from "../util/writeCodeFile.js";
import { CodeGen } from "./CodeGen.js";
import type { CodeGen } from "./CodeGen.js";
export class BaseFileGenerator {
protected readonly filePath: string;
protected readonly codeGen: CodeGen;
protected readonly imports = new Map<string, string>();
export class BaseFileGenerator<D> {
public readonly def: D;
public readonly filePath: string;
public readonly codeGen: CodeGen;
public readonly imports = new Map<string, string>();

@@ -14,8 +16,26 @@ constructor(

codeGen: CodeGen,
def: D,
) {
this.filePath = filePath;
this.codeGen = codeGen;
this.def = def;
}
ensureImportForType(type: ConjureApi.IType): void {
ensureImportForType(
type: ConjureApi.IType | ConjureApi.IServiceDefinition | ConjureApi.ITypeName,
): void {
if ("package" in type) {
const importPath = this.getImportModuleSpecifier(type);
this.imports.set(
`${type.package}.${type.name}`,
`import type { ${type.name} } from "${importPath}";`,
);
return;
}
if ("serviceName" in type) {
return this.ensureImportForType(type.serviceName);
}
switch (type.type) {

@@ -29,15 +49,6 @@ case "list":

case "reference":
let importPath = path.relative(
path.dirname(this.filePath),
this.codeGen.getFilePathForImport(type.reference),
);
if (importPath != ".") importPath = `./${importPath}`;
case "reference": {
return this.ensureImportForType(type.reference);
}
this.imports.set(
`${type.reference.package}.${type.reference.name}`,
`import { ${type.reference.name} } from "${importPath}";`,
);
return;
case "optional":

@@ -51,2 +62,17 @@ this.ensureImportForType(type.optional.itemType);

getImportModuleSpecifier(targetFile: string): string;
getImportModuleSpecifier(type: ConjureApi.ITypeName): string;
getImportModuleSpecifier(type: ConjureApi.ITypeName | string) {
let importPath = path.relative(
path.dirname(this.filePath),
this.codeGen.getFilePathForImport(type),
);
if (!importPath.startsWith(".")) importPath = `./${importPath}`;
if (this.codeGen.includeExtensions) {
return importPath;
}
}
getTypeForCode(type: ConjureApi.IType): string {

@@ -100,3 +126,4 @@ this.ensureImportForType(type);

protected async writeFile(body: string) {
async writeFile(body: string) {
await fs.promises.mkdir(path.dirname(this.filePath), { recursive: true });
await writeCodeFile(

@@ -103,0 +130,0 @@ this.filePath,

@@ -1,4 +0,4 @@

import * as ConjureApi from "conjure-api";
import type { IArgumentDefinition, IEndpointDefinition, IParameterType_Query } from "conjure-api";
export function calculateTemplatedUrlForEndpoint(endpoint: ConjureApi.IEndpointDefinition) {
export function calculateTemplatedUrlForEndpoint(endpoint: IEndpointDefinition) {
const queryArgs = endpoint.args.filter(isQueryArgument);

@@ -45,5 +45,5 @@ const queryPortion = queryArgs.length === 0

function isQueryArgument(
a: ConjureApi.IArgumentDefinition,
): a is ConjureApi.IArgumentDefinition & { paramType: ConjureApi.IParameterType_Query } {
a: IArgumentDefinition,
): a is IArgumentDefinition & { paramType: IParameterType_Query } {
return a.paramType.type === "query";
}

@@ -1,12 +0,27 @@

import * as ConjureApi from "conjure-api";
import type * as ConjureApi from "conjure-api";
import * as fs from "node:fs";
import * as path from "node:path";
import { HandleGenerateArgs } from "../cli/HandleGenerateArgs.js";
import type { HandleGenerateArgs } from "../cli/HandleGenerateArgs.js";
import { findCommonPrefix } from "../util/findCommonPrefix.js";
import { EndpointCodeFile } from "./EndpointCodeFile.js";
import { endpointCodeGenerator } from "./EndpointCodeFile.js";
import { enumCodeGenerator } from "./EnumCodeFile.js";
import { objectCodeGenerator } from "./ObjectCodeFile.js";
import { packageIndexCodeGenerator } from "./PackageCodeFile.js";
import { serviceCodeGenerator } from "./ServiceCodeFile.js";
import { spreadIntoTypes } from "./spreadIntoTypes.js";
import { typeAliasCodeGenerator } from "./TypeAliasCodeFile.js";
import { unionCodeGenerator } from "./UnionCodeFile.js";
const typeGenerators = {
object: objectCodeGenerator,
alias: typeAliasCodeGenerator,
enum: enumCodeGenerator,
union: unionCodeGenerator,
} as const;
export class CodeGen {
#outDir: string;
#ir: ConjureApi.IConjureDefinition;
#commonBase: string;
ir: ConjureApi.IConjureDefinition;
#commonPackageBase: string;
packages: Set<string>;

@@ -16,33 +31,90 @@ readonly includeExtensions: boolean;

constructor(ir: ConjureApi.IConjureDefinition, args: HandleGenerateArgs) {
this.#ir = ir;
this.ir = ir;
this.#outDir = args.outDir;
this.includeExtensions = args.includeExtensions;
const packages = new Set<string>();
this.packages = new Set<string>();
for (const t of ir.types) {
packages.add((t as any)[t.type].typeName.package);
// switch (t.type) {
// case "object":
// packages.add(t.object.typeName.package);
// continue;
// case "alias":
// packages.add(t.alias.typeName.package);
// continue;
// case "enum":
// packages.add(t.enum.typeName.package);
// continue;
// case "union":
// packages.add(t.union.typeName.package);
// continue;
// }
this.packages.add((t as any)[t.type].typeName.package);
}
this.#commonBase = findCommonPrefix(Array.from(packages));
this.#commonPackageBase = findCommonPrefix(Array.from(this.packages));
}
getPathDir(typeName: ConjureApi.ITypeName) {
async generate() {
const codeFiles: Array<() => Promise<void>> = [];
const completedPackages = new Set<string>([this.#commonPackageBase]);
// "root package"
codeFiles.push(
packageIndexCodeGenerator(
path.join(this.getPackageDir(this.#commonPackageBase), "index.ts"),
this,
{
packageName: this.#commonPackageBase,
},
),
);
for (const fullPackageName of this.packages) {
const partsAfterCommon = fullPackageName.substring(this.#commonPackageBase.length + 1).split(
".",
);
for (let i = 0; i < partsAfterCommon.length; i++) {
const packageName = this.#commonPackageBase + "."
+ partsAfterCommon.slice(0, i + 1).join(".");
if (completedPackages.has(packageName)) continue;
const packagePath = this.getPackageDir(packageName);
await fs.promises.mkdir(packagePath, { recursive: true });
codeFiles.push(
packageIndexCodeGenerator(path.join(packagePath, "index.ts"), this, {
packageName: packageName,
}),
);
}
}
for (const type of this.ir.types) {
// Typescript does not express this situation well
// `type[type.type]` is the value portion of the enum
const valuePortion = spreadIntoTypes(type);
codeFiles.push(typeGenerators[type.type](
this.getFilePath(valuePortion.typeName),
this,
valuePortion as any,
));
}
for (const service of this.ir.services) {
const serviceDir = this.getServiceDir(service);
await fs.promises.mkdir(serviceDir, {
recursive: true,
});
codeFiles.push(
serviceCodeGenerator(
this.getFilePath(service.serviceName),
this,
service,
),
);
for (const endpoint of service.endpoints) {
codeFiles.push(
endpointCodeGenerator(this.getEndpointPath(service, endpoint), this, endpoint),
);
}
}
await Promise.all(codeFiles.map(a => a()));
}
getPackageDir(fullPackage: string) {
return path.join(
this.#outDir,
...(typeName.package.substring(this.#commonBase.length).split(".")),
...fullPackage.substring(this.#commonPackageBase.length).split("."),
);

@@ -52,28 +124,31 @@ }

getFilePath(typeName: ConjureApi.ITypeName) {
return `${this.getFilePathWithoutExtension(typeName)}.ts`;
return `${path.join(this.getPackageDir(typeName.package), `${typeName.name}.ts`)}`;
}
getFilePathWithoutExtension(typeName: ConjureApi.ITypeName) {
return path.join(
this.getPathDir(typeName),
`${typeName.name}`,
);
getFilePathForImport(typeName: ConjureApi.ITypeName | string) {
const withoutExt = typeof typeName === "string"
? path.join(path.dirname(typeName), path.basename(typeName, path.extname(typeName)))
: path.join(this.getPackageDir(typeName.package), `${typeName.name}`);
return this.includeExtensions ? withoutExt + ".js" : withoutExt;
}
getFilePathForImport(typeName: ConjureApi.ITypeName) {
getServiceDir(service: ConjureApi.IServiceDefinition) {
return path.join(
this.getPathDir(typeName),
`${typeName.name}${this.includeExtensions ? ".js" : ""}`,
this.getPackageDir(service.serviceName.package),
service.serviceName.name,
);
}
async doThing(service: ConjureApi.IServiceDefinition, endpoint: ConjureApi.IEndpointDefinition) {
const serviceDir = path.join(this.getPathDir(service.serviceName), service.serviceName.name);
await fs.promises.mkdir(serviceDir, {
recursive: true,
});
const endpointFilePath = `${path.join(serviceDir, endpoint.endpointName)}.ts`;
const endpointCodeFile = new EndpointCodeFile(endpointFilePath, this, service, endpoint);
getEndpointPath(
service: ConjureApi.IServiceDefinition,
endpoint: ConjureApi.IEndpointDefinition,
) {
return `${
path.join(
this.getServiceDir(service),
endpoint.endpointName,
)
}.ts`;
}
}

@@ -1,39 +0,26 @@

import * as ConjureApi from "conjure-api";
import { BaseFileGenerator } from "./BaseFileGenerator.js";
import type { IEndpointDefinition, IType } from "conjure-api";
import dedent from "dedent";
import { calculateTemplatedUrlForEndpoint } from "./calculateTemplatedUrlForEndpoint.js";
import { CodeGen } from "./CodeGen.js";
import { generatorFactory } from "./generatorFactory.js";
export class EndpointCodeFile extends BaseFileGenerator {
#service: ConjureApi.IServiceDefinition;
#endpoint: ConjureApi.IEndpointDefinition;
export const endpointCodeGenerator = generatorFactory<IEndpointDefinition>(
async function() {
const templatedUrl = calculateTemplatedUrlForEndpoint(this.def);
constructor(
filePath: string,
codeGen: CodeGen,
service: ConjureApi.IServiceDefinition,
endpoint: ConjureApi.IEndpointDefinition,
) {
super(filePath, codeGen);
this.#service = service;
this.#endpoint = endpoint;
}
async generate() {
let endpointSource = "";
const templatedUrl = calculateTemplatedUrlForEndpoint(this.#endpoint);
for (const q of this.#endpoint.args) {
endpointSource += this.ensureImportForType(q.type);
for (const q of this.def.args) {
this.ensureImportForType(q.type);
}
if (this.#endpoint.returns) {
this.ensureImportForType(this.#endpoint.returns);
if (this.def.returns) {
this.ensureImportForType(this.def.returns);
}
const bodyArg = this.#endpoint.args.find(a => a.paramType.type === "body");
const bodyArg = this.def.args.find(a => a.paramType.type === "body");
const bodyArgContentType = getContentType(bodyArg?.type);
const acceptContentType = getContentType(this.#endpoint.returns);
const acceptContentType = getContentType(this.def.returns);
this.imports.set("#marker", `import {conjureFetch, type ConjureContext} from "conjure-lite"`);
this.imports.set(
"conjure-lite",
`import { conjureFetch, type ConjureContext } from "conjure-lite"`,
);

@@ -43,3 +30,3 @@ const args = [

templatedUrl,
`"${this.#endpoint.httpMethod}"`,
`"${this.def.httpMethod}"`,
bodyArg?.argName,

@@ -57,15 +44,15 @@ bodyArgContentType === "application/json" ? undefined : bodyArgContentType,

const functionSource = `
export async function ${this.#endpoint.endpointName}(ctx: ConjureContext, ${
this.#endpoint.args.map(a => `${a.argName}: ${this.getTypeForCode(a.type)}`)
}): Promise<${this.#endpoint.returns ? this.getTypeForCode(this.#endpoint.returns) : "void"}> {
return conjureFetch(${args.join(",")})
}
`;
const functionSource = dedent`
export async function ${this.def.endpointName}(ctx: ConjureContext, ${
this.def.args.map(a => `${a.argName}: ${this.getTypeForCode(a.type)}`).join(`, `)
}): Promise<${this.def.returns ? this.getTypeForCode(this.def.returns) : "void"}> {
return conjureFetch(${args.join(",")})
}
`;
await this.writeFile(functionSource);
}
}
},
);
export function getContentType(arg: ConjureApi.IType | undefined | null) {
export function getContentType(arg: IType | undefined | null) {
return (arg

@@ -78,4 +65,4 @@ && (isBinary(arg)

function isBinary(type: ConjureApi.IType) {
function isBinary(type: IType) {
return type.type === "primitive" && type.primitive === "BINARY";
}

@@ -1,30 +0,12 @@

import * as ConjureApi from "conjure-api";
import { BaseFileGenerator } from "./BaseFileGenerator.js";
import { CodeGen } from "./CodeGen.js";
import type { IEnumDefinition } from "conjure-api";
import { generatorFactory } from "./generatorFactory.js";
export class EnumCodeFile extends BaseFileGenerator {
def: ConjureApi.ITypeDefinition_Enum;
constructor(
filePath: string,
codeGen: CodeGen,
def: ConjureApi.ITypeDefinition_Enum,
) {
super(filePath, codeGen);
this.def = def;
}
get name() {
return this.def.enum.typeName.name;
}
async generate() {
export const enumCodeGenerator = generatorFactory<IEnumDefinition>(
async function() {
const { typeName: { name }, values } = this.def;
const source = `
export type ${this.name} = ${this.def.enum.values.map(v => `"${v.value}"`).join("|")}
`;
export type ${name} = ${values.map(({ value }) => `"${value}"`).join("|")};\n`;
await this.writeFile(source);
}
}
},
);

@@ -1,27 +0,12 @@

import * as ConjureApi from "conjure-api";
import { BaseFileGenerator } from "./BaseFileGenerator.js";
import { CodeGen } from "./CodeGen.js";
import type { IAliasDefinition } from "conjure-api";
import { generatorFactory } from "./generatorFactory.js";
export class TypeAliasCodeFile extends BaseFileGenerator {
name: string;
target: ConjureApi.IType;
constructor(
filePath: string,
codeGen: CodeGen,
name: string,
target: ConjureApi.IType,
) {
super(filePath, codeGen);
this.name = name;
this.target = target;
}
export const typeAliasCodeGenerator = generatorFactory<IAliasDefinition>(
async function() {
const { typeName: { name }, alias } = this.def;
async generate() {
const source = `
export type ${this.name} = ${this.getTypeForCode(this.target)}
`;
const source = `export type ${name} = ${this.getTypeForCode(alias)};\n`;
await this.writeFile(source);
}
}
},
);

@@ -1,40 +0,26 @@

import * as ConjureApi from "conjure-api";
import { BaseFileGenerator } from "./BaseFileGenerator.js";
import { CodeGen } from "./CodeGen.js";
import type { IFieldDefinition, IUnionDefinition } from "conjure-api";
import dedent from "dedent";
import { generatorFactory } from "./generatorFactory.js";
export class UnionCodeFile extends BaseFileGenerator {
def: ConjureApi.ITypeDefinition_Union;
export const unionCodeGenerator = generatorFactory<IUnionDefinition>(
async function() {
const { typeName: { name }, union } = this.def;
constructor(
filePath: string,
codeGen: CodeGen,
def: ConjureApi.ITypeDefinition_Union,
) {
super(filePath, codeGen);
const createUnionInterface = (u: IFieldDefinition) => {
return dedent`
export interface ${name}_${u.fieldName} {
type: "${u.fieldName}";
${u.fieldName}: ${this.getTypeForCode(u.type)}
}\n`;
};
this.def = def;
}
const source = dedent`
${union.map(createUnionInterface).join("\n")}
get name() {
return this.def.union.typeName.name;
}
async generate() {
const source = `
${
this.def.union.union.map(u => {
return `
export interface ${this.name}_${u.fieldName} {
type: "${u.fieldName}";
${u.fieldName}: ${this.getTypeForCode(u.type)}
} `;
})
}
export type ${this.name} = ${this.def.union.union.map(u => `${this.name}_${u.fieldName}`)}
export type ${name} = ${union.map(u => `${name}_${u.fieldName}`).join(" | ")}
`;
await this.writeFile(source);
}
}
},
);

@@ -1,2 +0,2 @@

import { ConjureContext } from ".";
import type { ConjureContext } from "./ConjureContext.js";

@@ -23,2 +23,3 @@ export async function conjureFetch<T>(

) {
//
} else {

@@ -28,3 +29,3 @@ body = JSON.stringify(body);

}
const response = await fetchFn(url, {
const response = await fetchFn(`${basePath}/${url}`, {
method,

@@ -31,0 +32,0 @@ credentials: "same-origin",

export { conjureFetch } from "./conjureFetch.js";
export interface ConjureContext {
fetchFn: typeof fetch;
basePath: string;
}
export type { ConjureContext } from "./ConjureContext.js";
export function findCommonPrefix(strings: Array<string>) {
for (let stringIndex = 0; stringIndex < strings[0].length; stringIndex++) {
for (let i = 1; i < strings.length; i++) {
if (strings[0][stringIndex] != strings[i][stringIndex]) {
return strings[0].substring(0, stringIndex);
const splitStrings = strings.map(s => s.split("."));
for (let i = 0; i < splitStrings[0].length; i++) {
for (let j = 1; j < splitStrings.length; j++) {
if (splitStrings[0][i] != splitStrings[j][i]) {
return splitStrings[0].slice(0, i).join(".");
}

@@ -7,0 +9,0 @@ }

import * as fs from "node:fs";
import { format } from "prettier";
export async function formatTs(code: string) {
try {
return await format(code, {
parser: "typescript",
tabWidth: 2,
printWidth: 80,
});
} catch (e) {
console.log(e);
return code;
}
}
export async function writeCodeFile(path: string, code: string) {
return await fs.promises.writeFile(
path,
await formatTs(code),
code,
"utf-8",
);
}

@@ -5,3 +5,3 @@ {

"outDir": "lib",
"lib": ["ESNext"],
"lib": ["ESNext", "webworker"],
"module": "Node16",

@@ -17,3 +17,8 @@ "target": "ESNext",

"forceConsistentCasingInFileNames": true,
"noEmitOnError": false
"noEmitOnError": false,
"types": [],
"paths": {
"conjure-lite": ["./src"],
"node:*": ["./node_modules/@types/node/*.d.ts"],
}
},

@@ -20,0 +25,0 @@ "exclude": ["adsrc/tmp/**/*"],

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc