@tai-kun/surrealdb
Version:
The SurrealDB SDK for JavaScript
118 lines (100 loc) • 2.94 kB
text/typescript
import { SurrealValueError } from "@tai-kun/surrealdb/errors";
import type { Formatter } from "@tai-kun/surrealdb/formatter";
import PreparedQuery from "./prepared-query";
import Raw, { type RawValue } from "./raw";
import Slot from "./slot";
export interface Surql {
<TValues extends unknown[] = unknown[]>(
texts: readonly string[] | TemplateStringsArray,
...values: TValues
): PreparedQuery<Extract<TValues[number], Slot>>;
raw: {
(value: RawValue): Raw;
};
slot: {
<const TName extends string, TValue = unknown>(
name: TName,
): Slot<TName, true, TValue | undefined>;
<const TName extends string, TValue>(
name: TName,
defaultValue: TValue,
): Slot<TName, false, TValue>;
};
}
export interface CreateSurqlConfig {
readonly formatter: Formatter;
readonly varPrefix?: string | undefined;
}
export default function createSurql(config: CreateSurqlConfig): Surql {
const {
formatter,
varPrefix = "_jst_", // JavaScript, Tagged Template Literals
} = config;
function surql(texts: readonly string[], ...values: unknown[]) {
if (texts.length - values.length !== 1) {
throw new SurrealValueError(
"template string",
`texts.length=${texts.length} and values.length=${values.length}`,
);
}
let text = "";
const vars: { [p: string]: unknown } = {};
const slots: Slot[] = [];
for (
let i = 0,
j: number,
v,
len = texts.length,
named: string[] = [];
i < len;
i++
) {
text += texts[i];
if (i + 1 === len) {
break;
}
for (j = 0; j < i; j++) {
if (Object.is(values[j], values[i])) {
break;
}
}
v = values[j];
if (v instanceof Slot) {
text += "$" + v.name;
if (j === i && named.indexOf(v.name) < 0) {
if ((v.name as string).startsWith(varPrefix)) {
throw new SurrealValueError(
`a variable name that do not start with "${varPrefix}"`,
v.name,
);
}
slots.push(v);
named.push(v.name);
}
} else if (v instanceof Raw) {
text += v.toString();
} else {
text += "$" + varPrefix + j;
if (!((varPrefix + j) in vars)) {
vars[varPrefix + j] = formatter.toEncoded?.(v) || v;
}
}
}
return new PreparedQuery(text, vars, slots, {
encodedText: formatter.toEncoded?.(text),
});
}
function raw(value: RawValue): Raw {
return new Raw(value);
}
function slot(...args: [name: string, defaultValue?: unknown]): Slot {
return args.length === 1
? new Slot(args[0], true, { formatter })
: new Slot(args[0], false, {
formatter,
defaultValue: formatter.toEncoded?.(args[1]) || args[1],
});
}
// @ts-expect-error
return Object.assign(surql, { raw, slot });
}