New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@balena/abstract-sql-to-typescript

Package Overview
Dependencies
Maintainers
3
Versions
240
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@balena/abstract-sql-to-typescript - npm Package Compare versions

Comparing version 3.0.0-build-3-x-2cef8ca234651a0e6b9281c91357d8002d37b594-1 to 3.0.0-build-3-x-42deabe6053cbc403879216aa353bc5f06c8414c-1

out/generate.d.ts

4

CHANGELOG.md

@@ -7,4 +7,6 @@ # Change Log

## 3.0.0 - 2024-04-22
## 3.0.0 - 2024-04-29
* Export a `Resource` type which all resources should conform to [Pagan Gazzard]
* Separate the generation code from the exported type helpers [Pagan Gazzard]
* Use types directly from sbvr-types [Pagan Gazzard]

@@ -11,0 +13,0 @@ * Expose read vs write selection in generated types [Pagan Gazzard]

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

import type { Types } from '@balena/sbvr-types';
export type { Types } from '@balena/sbvr-types';

@@ -10,4 +11,13 @@ export type Expanded<T> = Extract<T, any[]>;

};
import type { AbstractSqlModel } from '@balena/abstract-sql-compiler';
type RequiredModelSubset = Pick<AbstractSqlModel, 'tables' | 'relationships' | 'synonyms'>;
export declare const abstractSqlToTypescriptTypes: (m: RequiredModelSubset) => string;
type ReadTypes = Types[keyof Types]['Read'];
type WriteTypes = Types[keyof Types]['Write'];
export type Resource<T extends object = object> = {
Read: {
[key in keyof T]: ReadTypes | {
__id: ReadTypes;
} | Resource[];
};
Write: {
[key in keyof T]: WriteTypes;
};
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.abstractSqlToTypescriptTypes = void 0;
const odata_to_abstract_sql_1 = require("@balena/odata-to-abstract-sql");
const common_tags_1 = require("common-tags");
const typeHelpers = `import type { Types } from '@balena/abstract-sql-to-typescript';\n`;
const trimNL = new common_tags_1.TemplateTag((0, common_tags_1.replaceResultTransformer)(/^[\r\n]*|[\r\n]*$/g, ''));
const modelNameToCamelCaseName = (s) => s
.split(/[ -]/)
.map((p) => p[0].toLocaleUpperCase() + p.slice(1))
.join('');
const getReferencedInterface = (modelName, mode) => `${modelNameToCamelCaseName(modelName)}['${mode}']`;
const sqlTypeToTypescriptType = (m, f, mode) => {
if (!['ForeignKey', 'ConceptType'].includes(f.dataType) && f.checks) {
const inChecks = f.checks.find((checkTuple) => checkTuple[0] === 'In');
if (inChecks) {
const [, , ...allowedValues] = inChecks;
return allowedValues
.map(([type, value]) => (type === 'Text' ? `'${value}'` : value))
.join(' | ');
}
}
switch (f.dataType) {
case 'ConceptType':
case 'ForeignKey': {
const referencedInterface = getReferencedInterface(m.tables[f.references.resourceName].name, mode);
const referencedFieldType = `${referencedInterface}['${f.references.fieldName}']`;
if (mode === 'Write') {
return referencedFieldType;
}
const nullable = f.required ? '' : '?';
return `{ __id: ${referencedFieldType} } | [${referencedInterface}${nullable}]`;
}
default:
return `Types['${f.dataType}']['${mode}']`;
}
};
const fieldsToInterfaceProps = (m, fields, mode) => fields.map((f) => {
const nullable = f.required ? '' : ' | null';
return `${(0, odata_to_abstract_sql_1.sqlNameToODataName)(f.fieldName)}: ${sqlTypeToTypescriptType(m, f, mode)}${nullable};`;
});
const recurseRelationships = (m, relationships, inverseSynonyms, mode, currentTable, parentKey) => Object.keys(relationships).flatMap((key) => {
if (key === '$') {
const [localField, referencedField] = relationships.$;
if (currentTable.idField === localField && referencedField != null) {
const referencedTable = m.tables[referencedField[0]];
if (referencedTable != null) {
const referencedInterface = getReferencedInterface(referencedTable.name, mode);
const propDefinitons = [`${parentKey}?: ${referencedInterface}[];`];
const synonym = inverseSynonyms[(0, odata_to_abstract_sql_1.odataNameToSqlName)(parentKey)];
if (synonym != null) {
propDefinitons.push(`${(0, odata_to_abstract_sql_1.sqlNameToODataName)(synonym)}?: ${referencedInterface}[];`);
}
return propDefinitons;
}
}
return [];
}
return recurseRelationships(m, relationships[key], inverseSynonyms, mode, currentTable, `${parentKey}__${key.replace(/ /g, '_')}`);
});
const relationshipsToInterfaceProps = (m, table, mode) => {
const relationships = m.relationships[table.resourceName];
if (relationships == null) {
return [];
}
return Object.keys(relationships).flatMap((key) => {
if (key === 'has') {
return [];
}
const inverseSynonyms = Object.fromEntries(Object.entries(m.synonyms).map(([termForm, factType]) => [
factType,
termForm,
]));
return recurseRelationships(m, relationships[key], inverseSynonyms, mode, table, key.replace(/ /g, '_'));
});
};
const tableToInterface = (m, table) => {
return trimNL `
export interface ${modelNameToCamelCaseName(table.name)} {
Read: {
${[
...fieldsToInterfaceProps(m, table.fields, 'Read'),
...relationshipsToInterfaceProps(m, table, 'Read'),
].join('\n\t\t')}
}
Write: {
${[...fieldsToInterfaceProps(m, table.fields, 'Write')].join('\n\t\t')}
}
}
`;
};
const abstractSqlToTypescriptTypes = (m) => {
return trimNL `
${typeHelpers}
${Object.keys(m.tables)
.map((tableName) => {
const t = m.tables[tableName];
return tableToInterface(m, t);
})
.join('\n\n')}
`;
};
exports.abstractSqlToTypescriptTypes = abstractSqlToTypescriptTypes;
//# sourceMappingURL=index.js.map
{
"name": "@balena/abstract-sql-to-typescript",
"version": "3.0.0-build-3-x-2cef8ca234651a0e6b9281c91357d8002d37b594-1",
"version": "3.0.0-build-3-x-42deabe6053cbc403879216aa353bc5f06c8414c-1",
"description": "A translator for abstract sql into typescript types.",
"main": "out/index.js",
"types": "out/index.d.ts",
"exports": {
".": "./dist/index.js",
"./generate": "./dist/generate.js"
},
"scripts": {

@@ -19,3 +23,3 @@ "pretest": "npm run lint && npm run prepare",

"@balena/odata-to-abstract-sql": "^6.2.3",
"@balena/sbvr-types": "^7.1.0-build-read-write-types-66b9a012e242533372ce34a73e31f6e3aac93d91-1",
"@balena/sbvr-types": "^7.1.0",
"@types/node": "^20.11.24",

@@ -50,4 +54,4 @@ "common-tags": "^1.8.2"

"versionist": {
"publishedAt": "2024-04-22T16:41:54.807Z"
"publishedAt": "2024-04-29T17:01:09.598Z"
}
}

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

import type { Types } from '@balena/sbvr-types';
export type { Types } from '@balena/sbvr-types';

@@ -12,191 +13,12 @@

import type {
AbstractSqlField,
AbstractSqlModel,
AbstractSqlTable,
InNode,
Relationship,
RelationshipInternalNode,
RelationshipLeafNode,
} from '@balena/abstract-sql-compiler';
import {
odataNameToSqlName,
sqlNameToODataName,
} from '@balena/odata-to-abstract-sql';
import { replaceResultTransformer, TemplateTag } from 'common-tags';
type ReadTypes = Types[keyof Types]['Read'];
type WriteTypes = Types[keyof Types]['Write'];
type RequiredModelSubset = Pick<
AbstractSqlModel,
'tables' | 'relationships' | 'synonyms'
>;
const typeHelpers = `import type { Types } from '@balena/abstract-sql-to-typescript';\n`;
const trimNL = new TemplateTag(
replaceResultTransformer(/^[\r\n]*|[\r\n]*$/g, ''),
);
const modelNameToCamelCaseName = (s: string): string =>
s
.split(/[ -]/)
.map((p) => p[0].toLocaleUpperCase() + p.slice(1))
.join('');
const getReferencedInterface = (modelName: string, mode: Mode) =>
`${modelNameToCamelCaseName(modelName)}['${mode}']`;
const sqlTypeToTypescriptType = (
m: RequiredModelSubset,
f: AbstractSqlField,
mode: Mode,
): string => {
if (!['ForeignKey', 'ConceptType'].includes(f.dataType) && f.checks) {
const inChecks = f.checks.find(
(checkTuple): checkTuple is InNode => checkTuple[0] === 'In',
);
if (inChecks) {
const [, , ...allowedValues] = inChecks;
return allowedValues
.map(([type, value]) => (type === 'Text' ? `'${value}'` : value))
.join(' | ');
}
}
switch (f.dataType) {
case 'ConceptType':
case 'ForeignKey': {
const referencedInterface = getReferencedInterface(
m.tables[f.references!.resourceName].name,
mode,
);
const referencedFieldType = `${referencedInterface}['${f.references!.fieldName}']`;
if (mode === 'Write') {
return referencedFieldType;
}
const nullable = f.required ? '' : '?';
return `{ __id: ${referencedFieldType} } | [${referencedInterface}${nullable}]`;
}
default:
return `Types['${f.dataType}']['${mode}']`;
}
};
const fieldsToInterfaceProps = (
m: RequiredModelSubset,
fields: AbstractSqlField[],
mode: Mode,
): string[] =>
fields.map((f) => {
const nullable = f.required ? '' : ' | null';
return `${sqlNameToODataName(f.fieldName)}: ${sqlTypeToTypescriptType(
m,
f,
mode,
)}${nullable};`;
});
const recurseRelationships = (
m: RequiredModelSubset,
relationships: Relationship,
inverseSynonyms: Record<string, string>,
mode: Mode,
currentTable: AbstractSqlTable,
parentKey: string,
): string[] =>
Object.keys(relationships).flatMap((key) => {
if (key === '$') {
const [localField, referencedField] = (
relationships as RelationshipLeafNode
).$;
if (currentTable.idField === localField && referencedField != null) {
const referencedTable = m.tables[referencedField[0]];
if (referencedTable != null) {
const referencedInterface = getReferencedInterface(
referencedTable.name,
mode,
);
const propDefinitons = [`${parentKey}?: ${referencedInterface}[];`];
const synonym = inverseSynonyms[odataNameToSqlName(parentKey)];
if (synonym != null) {
propDefinitons.push(
`${sqlNameToODataName(synonym)}?: ${referencedInterface}[];`,
);
}
return propDefinitons;
}
}
return [];
}
return recurseRelationships(
m,
(relationships as RelationshipInternalNode)[key],
inverseSynonyms,
mode,
currentTable,
`${parentKey}__${key.replace(/ /g, '_')}`,
);
});
const relationshipsToInterfaceProps = (
m: RequiredModelSubset,
table: AbstractSqlTable,
mode: Mode,
): string[] => {
const relationships = m.relationships[table.resourceName];
if (relationships == null) {
return [];
}
return Object.keys(relationships).flatMap((key) => {
// We skip `has` a the top level as we omit it by convention
if (key === 'has') {
return [];
}
const inverseSynonyms = Object.fromEntries(
Object.entries(m.synonyms).map(([termForm, factType]) => [
factType,
termForm,
]),
);
return recurseRelationships(
m,
relationships[key],
inverseSynonyms,
mode,
table,
key.replace(/ /g, '_'),
);
});
};
const tableToInterface = (m: RequiredModelSubset, table: AbstractSqlTable) => {
return trimNL`
export interface ${modelNameToCamelCaseName(table.name)} {
export type Resource<T extends object = object> = {
Read: {
${[
...fieldsToInterfaceProps(m, table.fields, 'Read'),
...relationshipsToInterfaceProps(m, table, 'Read'),
].join('\n\t\t')}
}
[key in keyof T]: ReadTypes | { __id: ReadTypes } | Resource[];
};
Write: {
${[...fieldsToInterfaceProps(m, table.fields, 'Write')].join('\n\t\t')}
}
}
`;
[key in keyof T]: WriteTypes;
};
};
type Mode = 'Read' | 'Write';
export const abstractSqlToTypescriptTypes = (
m: RequiredModelSubset,
): string => {
return trimNL`
${typeHelpers}
${Object.keys(m.tables)
.map((tableName) => {
const t = m.tables[tableName];
return tableToInterface(m, t);
})
.join('\n\n')}
`;
};
import type { AbstractSqlModel } from '@balena/abstract-sql-compiler';
import { expect } from 'chai';
import { source } from 'common-tags';
import { abstractSqlToTypescriptTypes } from '../src';
import { abstractSqlToTypescriptTypes } from '../src/generate';

@@ -302,3 +302,3 @@ const test = (

id: Types['Serial']['Read'];
is_referenced_by__test?: Test['Read'][];
is_referenced_by__test?: Array<Test['Read']>;
}

@@ -321,4 +321,4 @@ Write: {

references__other: { __id: Other['Read']['id'] } | [Other['Read']];
test__has__tag_key?: TestTag['Read'][];
test_tag?: TestTag['Read'][];
test__has__tag_key?: Array<TestTag['Read']>;
test_tag?: Array<TestTag['Read']>;
}

@@ -325,0 +325,0 @@ Write: {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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