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

@embracesql/postgres

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@embracesql/postgres - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

src/generator/pgtype/base/bool.ts

9

package.json
{
"name": "@embracesql/postgres",
"version": "0.0.2",
"version": "0.0.3",
"description": "EmbraceSQL shared library for talking to postgres. Used in Node.",

@@ -13,10 +13,9 @@ "type": "module",

"dependencies": {
"@embracesql/shared": "^0.0.2",
"@embracesql/shared": "^0.0.3",
"glob": "^10.3.10",
"parsimmon": "^1.18.1",
"pg-connection-string": "^2.6.2",
"postgres": "^3.4.3",
"prettier": "^3.1.0"
"prettier": "3.1.1"
},
"gitHead": "ae6361bd48bb27798c635cdb30e0990d5c90960c"
"gitHead": "ce8c43da83ab6e340d337431a8d18e652a127cbd"
}
import { Context } from "../../../context";
import { PGCatalogType } from "../pgcatalogtype";
import { GenerationContext } from "@embracesql/shared";
export class PGNumber extends PGCatalogType {
/**
* Numbers in the database flow from here.
*
* One thing to keep in mind, JS/TS have a powerful -- but only the
* one number type. Postgres is closer to C byte width style numerics.
*/
export class PGTypeNumber extends PGCatalogType {
typescriptTypeDefinition(context: Context) {

@@ -12,2 +19,11 @@ console.assert(context);

typescriptTypeParser(context: GenerationContext) {
console.assert(context);
return `
parse${this.typescriptName}(from: string) {
return Number.parseFloat(from);
}
`;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any

@@ -33,1 +49,56 @@ serializeToPostgres(context: Context, x: any) {

}
/**
* Large integers. Surprise! JS does have a type for this too.
*/
export class PGTypeBigInt extends PGTypeNumber {
typescriptTypeParser(context: GenerationContext) {
console.assert(context);
return `
parse${this.typescriptName}(from: string) {
return BigInt(from);
}
`;
}
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
export type ${this.typescriptName} = BigInt;
`;
}
}
export class PGTypeBytea extends PGCatalogType {
typescriptTypeParser(context: GenerationContext) {
console.assert(context);
return `
parse${this.typescriptName}(from: string) {
return new Uint8Array(JSON.parse(from));
}
`;
}
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
export type ${this.typescriptName} = Uint8Array;
`;
}
}
export class PGTypeVector extends PGCatalogType {
typescriptTypeParser(context: GenerationContext) {
console.assert(context);
return `
parse${this.typescriptName}(from: string) {
return new Float32Array(JSON.parse(from));
}
`;
}
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
export type ${this.typescriptName} = Float32Array;
`;
}
}

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

import { PGNumber } from "../base/number";
import { PGTypeNumber } from "../base/number";
import { registerOverride } from "./_overrides";

@@ -20,2 +20,2 @@

registerOverride("interval", PGNumber);
registerOverride("interval", PGTypeNumber);

@@ -1,5 +0,5 @@

import { PGNumber } from "../base/number";
import { PGTypeNumber } from "../base/number";
import { registerOverride } from "./_overrides";
class OID extends PGNumber {}
class OID extends PGTypeNumber {}

@@ -6,0 +6,0 @@ registerOverride("oid", OID);

import { Context } from "../../../context";
import { PGCatalogType } from "../pgcatalogtype";
import { registerOverride } from "./_overrides";
import { UUID } from "@embracesql/shared";
import { GenerationContext, UUID } from "@embracesql/shared";
class PGUUID extends PGCatalogType {
typescriptTypeParser(context: GenerationContext) {
console.assert(context);
return `
parse${this.typescriptName}(from: string) {
return new UUID(from);
}
`;
}
typescriptTypeDefinition(context: Context) {

@@ -8,0 +16,0 @@ console.assert(context);

@@ -0,5 +1,6 @@

import { GeneratesTypeScriptParser, GenerationContext } from "@embracesql/shared";
import { Context, PostgresTypecast } from "../../context";
import { asDocComment } from "../../util";
import { pascalCase } from "change-case";
import { CatalogRow } from "./pgtype";
import { pascalCase } from "change-case";

@@ -9,3 +10,3 @@ /**

*/
export class PGCatalogType {
export class PGCatalogType implements GeneratesTypeScriptParser {
/**

@@ -29,2 +30,14 @@ * Base constructions picks out the name.

/**
* The default parser doesn't do much -- just echoes a string.
*/
typescriptTypeParser(context: GenerationContext) {
console.assert(context)
return `
parse${this.typescriptName}(from: string) {
return from;
}
`
}
/**
* Convention is pascal case for TS.

@@ -53,3 +66,4 @@ */

/**
* Convention is snake case in PG.
* Convention is snake case in PG, separating namespace(schema) from
* the object (type, table, proc...) with a `.`.
*/

@@ -56,0 +70,0 @@ get postgresName() {

@@ -21,3 +21,3 @@ import { Context, TypeFactoryContext } from "../../context";

/**
* Collect up all indexes in the posgres catalog.
* Collect up all indexes in the postgres catalog.
*/

@@ -24,0 +24,0 @@ export class PGIndexes {

@@ -7,3 +7,3 @@ import { Context } from "../../context";

import { PGTypes } from "./pgtype";
import { DatabaseNode, SchemaNode, TablesNode } from "@embracesql/shared";
import { DatabaseNode, SchemaNode, TablesNode, TypeNode } from "@embracesql/shared";
import { pascalCase } from "change-case";

@@ -73,2 +73,4 @@

this.tables.forEach((t) => t.addToAST(tables));
// all types in the namespace
this.types.forEach(t => schema.children.push(new TypeNode(schema, t)))
}

@@ -75,0 +77,0 @@

@@ -5,12 +5,8 @@ import { Context, PostgresProcTypecast } from "../../../context";

import { PGTypes } from "../pgtype";
import { generateRequestType } from "./generateRequestType";
import { generateResponseType } from "./generateResponseType";
import {
attributeSeperator,
compositeAttribute,
endComposite,
interleave,
ObjectParser,
startComposite,
} from "../pgtypecomposite";
import { generateRequestType } from "./generateRequestType";
import { generateResponseType } from "./generateResponseType";
parseObjectWithAttributes,
} from "@embracesql/shared";
import { camelCase, pascalCase } from "change-case";

@@ -184,9 +180,3 @@ import parsimmon from "parsimmon";

const args = [
startComposite,
...interleave(attributes, attributeSeperator),
endComposite,
];
return parsimmon.seqObj<ObjectParser>(...args).tryParse(x);
return parseObjectWithAttributes(attributes, x);
}

@@ -193,0 +183,0 @@

import { Context, TypeFactoryContext } from "../../context";
import { PGCatalogType } from "./pgcatalogtype";
import { CatalogRow } from "./pgtype";
import {
DELIMITER,
arrayAttribute,
escapeArrayValue,
} from "@embracesql/shared";
import { pascalCase } from "change-case";
import parsimmon from "parsimmon";

@@ -45,3 +49,3 @@ /**

// quick escape with regex
return value ? escapeValue.tryParse(`${value}`) : "";
return value ? escapeArrayValue.tryParse(`${value}`) : "";
});

@@ -69,67 +73,1 @@ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions

}
/**
* Parsing array types -- from the docs:
* ... The decoration consists of curly braces ({ and }) around the array value plus delimiter characters between adjacent items.
*/
const startArray = parsimmon.string("{");
const endArray = parsimmon.string("}");
const DELIMITER = ",";
const delimiter = parsimmon.string(DELIMITER);
const separators = parsimmon.oneOf("{},");
/**
* Here is a simple one -- from the docs:
*
* ... If the value written for an element is NULL (in any case variant), the element is taken to be NULL.
*/
const NULLisNull = parsimmon.string("NULL").result(null);
/**
* And quoting rules -- from the docs:
* ... The array output routine will put double quotes around element values if they are empty strings, contain curly braces, delimiter characters, double quotes, backslashes, or white space, or match the word NULL.
*/
const REQUIRES_ESCAPE_IN_VALUE = String.raw`"\{},"`;
const requiresEscapeInValue = parsimmon.oneOf(REQUIRES_ESCAPE_IN_VALUE);
const neverRequiresEscape = parsimmon.noneOf(REQUIRES_ESCAPE_IN_VALUE);
export const escaped = parsimmon
.string("\\")
.times(1)
.then(requiresEscapeInValue);
const addEscape = requiresEscapeInValue.map((m) => `\\${m}`);
const escapeValue = parsimmon
.alt(neverRequiresEscape, addEscape)
.many()
.tie()
.map((m) => `"${m}"`);
/**
* And you can escape everything -- from the docs:
* ... you can avoid quotes and use backslash-escaping to protect all data characters that would otherwise be taken as array syntax.
*/
const bareString = parsimmon.alt(neverRequiresEscape, escaped).atLeast(1).tie();
/**
* And an element can be quoted - looking at actual DB results, this
* is the common case.
*/
const quotedString = parsimmon
.alt(neverRequiresEscape, escaped, separators)
.many()
.wrap(parsimmon.string('"'), parsimmon.string('"'))
.tie();
/**
* An individual element can then be quoted, unquoted, or a NULL.
*
* Put the null first so it'll be 'greedy' and capture before bareString.
*/
const arrayElement = parsimmon.alt(quotedString, NULLisNull, bareString);
/**
* Full assembled array value parser.
*/
export const arrayAttribute = arrayElement
.sepBy(delimiter)
.wrap(startArray, endArray);
import { Context, TypeFactoryContext } from "../../context";
import { asDocComment } from "../../util";
import { PGNumber } from "./base/number";
import { PGTypeBool } from "./base/bool";
import {
PGTypeBigInt,
PGTypeBytea,
PGTypeNumber,
PGTypeVector,
} from "./base/number";
import { PGTypeText, PGTypeTextArray } from "./base/text";
import { PGTypeUri } from "./base/uri";
import { PGCatalogType } from "./pgcatalogtype";

@@ -8,4 +16,5 @@ import { CatalogRow } from "./pgtype";

/**
* Base types defined by PostgreSQL. This is another factory
* that dispenses type specific code generation strategies.
* Base types defined by PostgreSQL.
*
* This is another factory that dispenses type specific code generation strategies.
*/

@@ -17,4 +26,2 @@ export class PGTypeBase extends PGCatalogType {

return new PGTypeTid(catalog);
case "uuid":
return new PGTypeUuid(catalog);
case "xml":

@@ -82,3 +89,3 @@ return new PGTypeText(catalog);

case "N":
return new PGNumber(catalog);
return new PGTypeNumber(catalog);
case "S":

@@ -108,85 +115,2 @@ return new PGTypeText(catalog);

class PGTypeText extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = string;
`;
}
}
class PGTypeTextArray extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = Array<string>;
`;
}
}
class PGTypeBigInt extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = BigInt;
`;
}
}
class PGTypeBool extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = boolean;
`;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
serializeToPostgres(context: Context, x: any) {
// string it -- yeah I know this is strange -- but it is how the
// postges protocol works
// https://github.com/porsager/postgres/blob/master/src/types.js#L25
return x ? "t" : "f";
}
parseFromPostgres(context: Context, x: string | null) {
// I've seen text encoding come back with t, f, true, false
if (["t", "true"].includes(x as string)) return true;
// false is the default
return false;
}
}
class PGTypeBytea extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = Uint8Array;
`;
}
}
class PGTypeVector extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = Float32Array;
`;
}
}
class PGTypeUuid extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = string;
`;
}
}
class PGTypeInet extends PGTypeBase {

@@ -249,11 +173,1 @@ typescriptTypeDefinition(context: Context) {

}
class PGTypeUri extends PGTypeBase {
typescriptTypeDefinition(context: Context) {
console.assert(context);
return `
${asDocComment(this.comment)}
export type ${this.typescriptName} = URL;
`;
}
}

@@ -6,18 +6,10 @@ import { Context, TypeFactoryContext } from "../../context";

import { CatalogRow } from "./pgtype";
import {
DELIMITER,
compositeAttribute,
escapeCompositeValue,
parseObjectWithAttributes,
} from "@embracesql/shared";
import { camelCase } from "change-case";
import parsimmon from "parsimmon";
export const interleave = <T, U>(arr: T[], separator: U) => {
const ret = new Array<T | U>();
arr.forEach((e, i) => {
ret.push(e);
if (i < arr.length - 1) ret.push(separator);
});
return ret;
};
export interface ObjectParser {
[name: string]: string | null;
}
/**

@@ -161,3 +153,3 @@ * Composite types are built up with other types into name:type

// quick escape with regex
return value ? escapeValue.tryParse(`${value}`) : "";
return value ? escapeCompositeValue.tryParse(`${value}`) : "";
});

@@ -174,82 +166,13 @@ // eslint-disable-next-line @typescript-eslint/restrict-template-expressions

// and chain along to the parser for that type
const attributes = this.attributes.map(
(a) =>
[
camelCase(a.attribute.attname),
compositeAttribute.map((parsedAttributeText) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
context
.resolveType(a.attribute.atttypid)
.parseFromPostgres(context, parsedAttributeText),
),
] as [string, parsimmon.Parser<string | null>],
);
const args = [
startComposite,
...interleave(attributes, attributeSeperator),
endComposite,
];
return parsimmon.seqObj<ObjectParser>(...args).tryParse(x);
const attributes = this.attributes.map((a) => [
camelCase(a.attribute.attname),
compositeAttribute.map((parsedAttributeText) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
context
.resolveType(a.attribute.atttypid)
.parseFromPostgres(context, parsedAttributeText),
),
]);
return parseObjectWithAttributes(attributes, x);
}
}
/**
* An empty string parses to a null. This will show up in composites like
* (,hello) -- which is null, hello
*/
const emptyIsNull = parsimmon.string("").result(null);
/**
* Escaping is done with backslash -- per the docs:
* ... To put a double quote or backslash in a quoted composite field value, precede it with a backslash. (Also, a pair of double quotes within a double-quoted field value is taken to represent a double quote character, analogously to the rules for single quotes in SQL literal strings.)
* ...Alternatively, you can avoid quoting and use backslash-escaping to protect all data characters that would otherwise be taken as composite syntax.
*/
const SEPARATORS = `(),`;
const DELIMITER = `,`;
const separators = parsimmon.oneOf(SEPARATORS);
export const startComposite = parsimmon.string("(");
export const endComposite = parsimmon.string(")");
export const attributeSeperator = parsimmon.string(",");
const REQUIRES_ESCAPE_IN_VALUE = `"\\${SEPARATORS}`;
const requiresEscapeInValue = parsimmon.oneOf(REQUIRES_ESCAPE_IN_VALUE);
const neverRequiresEscape = parsimmon.noneOf(REQUIRES_ESCAPE_IN_VALUE);
export const escaped = parsimmon
.string("\\")
.times(1)
.then(requiresEscapeInValue);
const addEscape = requiresEscapeInValue.map((m) => `\\${m}`);
const escapeValue = parsimmon
.alt(neverRequiresEscape, addEscape)
.many()
.tie()
.map((m) => `"${m}"`);
/**
* From the PG docs
* ... In particular, fields containing parentheses, commas, double quotes, or backslashes must be double-quoted.
*
* meaning -- if you are anything other than these characters -- you are a value.
*/
const bareString = parsimmon.alt(neverRequiresEscape, escaped).atLeast(1).tie();
/**
* Tales from the docs:
* ... Also, a pair of double quotes within a double-quoted field value is taken to represent a double quote character, analogously to the rules for single quotes in SQL literal strings.
*/
const pairOfQuotes = parsimmon.string('"').times(2).result('"');
const quotedString = parsimmon
.alt(neverRequiresEscape, pairOfQuotes, escaped, separators)
.many()
.wrap(parsimmon.string('"'), parsimmon.string('"'))
.tie();
/**
* Parse a composite attribute. This is exposed to allow unit testing.
*/
export const compositeAttribute = parsimmon.alt(
quotedString,
bareString,
emptyIsNull,
);

@@ -18,2 +18,3 @@ import { Context, TypeFactoryContext } from "../../context";

const type = context.resolveType(this.catalog.rngsubtype)!;
// array with two elements is the nearst-to-a-tuple in JS
return `

@@ -20,0 +21,0 @@ export type ${this.typescriptName} = [${type.typescriptNameWithNamespace(

@@ -83,4 +83,17 @@ import { GenerationContext } from "..";

// parsing from strings into TS types.
generationBuffer.push(`// begin string parsers`);
context.handlers = {
[ASTKind.Schema]: NamespaceVisitor,
[ASTKind.Type]: {
before: async (context, node) => {
return `export function ${node.parser.typescriptTypeParser(context)}`;
},
},
};
// no skipping schemas for parsing
generationBuffer.push(await context.database.visit({ ...context, skipSchemas: [] }));
generationBuffer.push(`// end string parsers`);
// primary key 'pickers' used to debounce and hash objects
generationBuffer.push(`// begin primary key pickers`);

@@ -87,0 +100,0 @@ context.handlers = {

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