vue-postgrest
Version:
Vue.js Component providing PostgREST integration
882 lines (881 loc) • 28.8 kB
JavaScript
import Vue from "vue";
function mapAliasesFromSelect(select = [], data) {
const kvPairs = Array.isArray(select) ? select.map((k) => [k, true]) : typeof select === "string" ? select.split(",").map((k) => [k, true]) : Object.entries(select);
const alias2column = new Map(
kvPairs.map(([k, v]) => {
if (!v) return false;
const [alias, column] = k.split(":");
return [alias, column ?? alias];
}).filter(Boolean)
);
return Object.fromEntries(Object.entries(data).map(([alias, value]) => [alias2column.get(alias) ?? alias, value]));
}
function reflectHelper(keys, ret, target, property, ...args) {
if (keys.includes(property)) return ret;
return Reflect[this](target, property, ...args);
}
const $diff = Symbol("diff");
const $freeze = Symbol("freeze");
const $isDiffProxy = Symbol("isDiffProxy");
function createDiffProxy(target, parentDirty = false) {
const base = Array.isArray(target) ? [] : {};
copy(target, base);
return new Proxy(target, {
get(target2, property, receiver) {
switch (property) {
case $diff:
return Object.fromEntries(
Object.entries(target2).filter(([k, v]) => v !== base[k] || v && v[$isDiffProxy] && v.$isDirty)
);
case $freeze:
return () => {
parentDirty = false;
copy(target2, base, $freeze);
};
case $isDiffProxy:
return true;
case "$isDirty":
if (parentDirty) return true;
if (Array.isArray(target2)) {
if (target2.length !== base.length) return true;
return target2.filter((v, k) => v !== base[k] || v && v[$isDiffProxy] && v.$isDirty).length > 0;
} else {
if (Object.keys(base).filter((k) => !(k in target2)).length > 0) return true;
return Object.entries(target2).filter(([k, v]) => v !== base[k] || v && v[$isDiffProxy] && v.$isDirty).length > 0;
}
case "$reset":
return () => copy(base, target2, "$reset");
}
return Reflect.get(target2, property, receiver);
},
set(target2, property, value, receiver) {
if (typeof value === "object" && value !== null && !value[$isDiffProxy]) {
value = createDiffProxy(value, true);
}
return reflectHelper.call("set", [$diff, $freeze, $isDiffProxy, "$isDirty", "$reset"], false, target2, property, value, receiver);
},
defineProperty: reflectHelper.bind("defineProperty", [$diff, $freeze, $isDiffProxy, "$isDirty", "$reset"], false),
deleteProperty: reflectHelper.bind("deleteProperty", [$diff, $freeze, $isDiffProxy, "$isDirty", "$reset"], false),
getOwnPropertyDescriptor: reflectHelper.bind("getOwnPropertyDescriptor", [$diff, $freeze, $isDiffProxy, "$isDirty", "$reset"], void 0),
has: reflectHelper.bind("has", [$diff, $freeze, $isDiffProxy, "$isDirty", "$reset"], true)
});
}
function copy(target, base, recurse) {
Object.entries(target).forEach(([k, v]) => {
if (typeof v === "object" && v !== null) {
if (v[$isDiffProxy]) {
v[recurse]?.();
} else {
target[k] = createDiffProxy(v);
}
}
base[k] = target[k];
});
}
function createPKQuery(pkColumns = [], data = {}) {
try {
if (pkColumns.length === 0) throw new PrimaryKeyError();
return pkColumns.reduce((query, col) => {
if (data[col] === void 0 || data[col] === null) {
throw new PrimaryKeyError(col);
}
query[col + ".eq"] = data[col];
return query;
}, {});
} catch (e) {
if (e instanceof PrimaryKeyError) {
return e;
} else {
throw e;
}
}
}
class ObservableFunction extends Function {
constructor(fn) {
super();
return new Proxy(createReactivePrototype(this, this), {
apply: async (target, thisArg, argumentsList) => {
const controller = new AbortController();
this.pending.push(controller);
try {
const ret = await fn(controller.signal, ...argumentsList);
this.clear();
this.hasReturned = true;
return ret;
} catch (e) {
this.errors.push(e);
throw e;
} finally {
this.pending = this.pending.filter((p) => p !== controller);
}
}
});
}
hasReturned = false;
pending = [];
get isPending() {
return this.pending.length > 0;
}
errors = [];
get hasError() {
return this.errors.length > 0;
}
clear(...args) {
if (args.length) {
this.errors = this.errors.filter((e, i) => !args.includes(e) && !args.includes(i));
} else {
this.errors = [];
this.hasReturned = false;
}
}
}
function createReactivePrototype(target, boundThis) {
const reactiveBase = Vue.observable(Array.isArray(target) ? [] : {});
Object.defineProperties(target, Object.getOwnPropertyDescriptors(reactiveBase));
target.__ob__.value = target;
const targetProto = Object.getPrototypeOf(target);
const { constructor, ...props } = Object.getOwnPropertyDescriptors(targetProto);
const keys = Object.keys(props);
const reactiveProto = Object.getPrototypeOf(reactiveBase);
Object.setPrototypeOf(target, Object.create(targetProto, {
...Object.getOwnPropertyDescriptors(reactiveProto),
constructor
}));
const makeEnumerableAndObservableProxy = new Proxy(target, {
defineProperty: (target2, key, descriptor) => {
descriptor.enumerable = true;
if (descriptor.value instanceof Function) {
descriptor.value = descriptor.value.bind(boundThis);
if (descriptor.value[Symbol.toStringTag] === "AsyncFunction") {
descriptor.value = new ObservableFunction(descriptor.value);
}
}
return Reflect.defineProperty(target2, key, descriptor);
}
});
Object.defineProperties(makeEnumerableAndObservableProxy, props);
const makeNonEnumerableProxy = new Proxy(target, {
defineProperty: (target2, key, descriptor) => {
if (keys.includes(key)) {
descriptor.enumerable = false;
}
return Reflect.defineProperty(target2, key, descriptor);
}
});
function walk(obj) {
const keys2 = Object.keys(obj);
for (let i = 0; i < keys2.length; i++) {
const key = keys2[i];
Vue.util.defineReactive(obj, key);
}
}
walk(makeNonEnumerableProxy);
return new Proxy(target, {
defineProperty: reflectHelper.bind("defineProperty", keys, false),
deleteProperty: reflectHelper.bind("deleteProperty", keys, false),
getOwnPropertyDescriptor: reflectHelper.bind("getOwnPropertyDescriptor", keys, void 0),
ownKeys: (target2) => {
return Reflect.ownKeys(target2).filter((k) => !keys.includes(k));
},
set: reflectHelper.bind("set", keys, false)
});
}
function splitToObject(str, fieldDelimiter = ",", kvDelimiter = "=") {
return str.split(fieldDelimiter).reduce((acc, field) => {
const parts = field.split(kvDelimiter);
acc[parts[0].trim()] = parts[1] ? parts[1].replace(/^["\s]+|["\s]+$/g, "") : void 0;
return acc;
}, {});
}
class FetchError extends Error {
constructor(resp, body) {
super(resp.statusText);
this.name = "FetchError";
this.resp = resp;
this.status = resp.status;
Object.assign(this, body);
}
}
class AuthError extends FetchError {
constructor(resp, body) {
super(resp, body);
this.name = "AuthError";
Object.assign(this, splitToObject(resp.headers.get("WWW-Authenticate").replace(/^Bearer /, "")));
}
}
class PrimaryKeyError extends Error {
constructor(pk) {
super(`Primary key not found ${pk ?? ""}`);
this.name = "PrimaryKeyError";
}
}
class SchemaNotFoundError extends Error {
constructor(apiRoot, err) {
super("No openapi definition found for api-root: " + apiRoot);
this.name = SchemaNotFoundError;
this.causedBy = err;
}
}
async function throwWhenStatusNotOk(resp) {
if (!resp.ok) {
let body = {};
try {
body = await resp.json();
} catch {
}
if (resp.headers.get("WWW-Authenticate")) {
throw new AuthError(resp, body);
}
throw new FetchError(resp, body);
}
return resp;
}
class GenericModel {
#options;
#proxy;
constructor(options, data) {
this.#options = options;
Object.assign(this, data);
this.#proxy = createReactivePrototype(createDiffProxy(this), this);
return this.#proxy;
}
async #request({ method, keepChanges = false, needsQuery = true }, signal, opts, ...data) {
await this.#options.route.$ready;
const { columns, ...options } = opts;
const query = { select: this.#options.select };
if (needsQuery) {
const q = this.#options.query;
if (!q) throw new PrimaryKeyError();
if (q instanceof PrimaryKeyError) throw q;
Object.assign(query, q);
}
if (columns) {
if (this.#options.route.columns) {
query.columns = columns.filter((c) => this.#options.route.columns.includes(c));
} else {
query.columns = columns;
}
}
data = data.map((data2) => {
return Object.fromEntries(
Object.entries(mapAliasesFromSelect(this.#options.select, data2)).filter(([col, v]) => !this.#options.route.columns || this.#options.route.columns.includes(col))
);
});
const resp = await this.#options.route[method](query, { ...options, accept: "single", signal }, ...data);
let body;
try {
body = await resp.json();
} catch {
if (!resp.headers.get("Location")) return;
const loc = new URLSearchParams(resp.headers.get("Location").replace(/^\/[^?]+\?/, ""));
return Object.fromEntries(Array.from(loc.entries()).map(([key, value]) => [key, value.replace(/^eq\./, "")]));
}
if (keepChanges) {
const diff = this.#proxy[$diff];
Object.entries(body).forEach(([key, value]) => Vue.set(this.#proxy, key, value));
this.#proxy[$freeze]();
Object.entries(diff).forEach(([key, value]) => Vue.set(this.#proxy, key, value));
} else {
Object.entries(body).forEach(([key, value]) => Vue.set(this.#proxy, key, value));
this.#proxy[$freeze]();
}
return body;
}
async $get(signal, opts = {}) {
const { keepChanges, ...options } = opts;
return this.#request({ method: "get", keepChanges }, signal, options);
}
async $post(signal, opts = {}) {
const options = { return: "representation", ...opts };
const body = await this.#request({ method: "post", needsQuery: false }, signal, options, this.#proxy);
if (body) {
this.#options.query = createPKQuery(this.#options.route.pks, mapAliasesFromSelect(this.#options.query?.select, body));
}
return body;
}
async $put(signal, opts) {
const options = { return: "representation", ...opts };
return this.#request({ method: "put" }, signal, options, this.#proxy);
}
async $patch(signal, opts, data = {}) {
const options = { return: "representation", ...opts };
if (!data || typeof data !== "object") {
throw new Error("Patch data must be an object.");
}
const patchData = Object.assign(
{},
this.#proxy[$diff],
data
);
if (Object.keys(patchData).length === 0) {
return this.#proxy;
}
return this.#request({ method: "patch" }, signal, options, patchData);
}
async $delete(signal, options = {}) {
return this.#request({ method: "delete" }, signal, options);
}
}
class GenericCollection extends Array {
#options;
#proxy;
#range = {};
constructor(options, ...models) {
super();
this.#options = options;
this.#proxy = new Proxy(createReactivePrototype(this, this), {
get: (target, property, receiver) => {
if (property === "$range") return this.#range;
return Reflect.get(target, property, receiver);
},
set: (target, property, value, receiver) => {
if (property === "length") return Reflect.set(target, property, value, receiver);
if (typeof value !== "object" || !value) {
throw new Error("Can only add objects to GenericCollection");
}
return Reflect.set(
target,
property,
new GenericModel(
{
route: this.#options.route,
select: this.#options.query?.select,
query: createPKQuery(this.#options.route.pks, mapAliasesFromSelect(this.#options.query?.select, value))
},
value
),
receiver
);
}
});
this.#proxy.push(...models);
return this.#proxy;
}
map(...args) {
return Array.from(this).map(...args);
}
async $get(signal, opts = {}) {
await this.#options.route.$ready;
const { accept, route, query = {}, ...options } = Object.assign({}, this.#options, opts);
const resp = await this.#options.route.get(query, { ...options, signal });
if (resp.headers.get("Content-Range")) {
const [bounds, total] = resp.headers.get("Content-Range").split("/");
const [first, last] = bounds.split("-");
this.#range = {
totalCount: total === "*" ? void 0 : parseInt(total, 10),
first: parseInt(first, 10),
last: isNaN(parseInt(last, 10)) ? void 0 : parseInt(last, 10)
};
}
const body = await resp.json();
this.length = 0;
this.#proxy.push(...body);
return body;
}
$new(data) {
const newIndex = this.#proxy.push(data) - 1;
return this.#proxy[newIndex];
}
}
const mixin = {
data() {
return {
pg: null
};
},
watch: {
pgConfig: {
deep: true,
immediate: true,
async handler(cfg) {
if (!cfg) return;
const makeOptions = () => Object.defineProperties({}, {
route: {
get: () => this.$postgrest(this.pgConfig.apiRoot, this.pgConfig.token).$route(this.pgConfig.route),
enumerable: true
},
query: {
get: () => this.pgConfig.query,
enumerable: true
},
limit: {
get: () => this.pgConfig.limit,
enumerable: true
},
offset: {
get: () => this.pgConfig.offset,
enumerable: true
},
count: {
get: () => this.pgConfig.count,
enumerable: true
}
});
if (cfg.single && !(this.pg instanceof GenericModel)) {
this.pg = new GenericModel(makeOptions(), {});
} else if (!cfg.single && !(this.pg instanceof GenericCollection)) {
this.pg = new GenericCollection(makeOptions());
}
if (this.pg instanceof GenericCollection || cfg.query) {
try {
await this.pg?.$get();
} catch (e) {
if (this.$options.onError) {
this.$options.onError.forEach((hook) => hook.call(this, e));
}
}
}
}
}
}
};
const Postgrest = {
name: "Postgrest",
mixins: [mixin],
props: {
route: {
type: String,
required: true
},
apiRoot: {
type: String
},
token: {
type: String
},
query: {
type: Object
},
single: {
type: Boolean
},
limit: {
type: Number
},
offset: {
type: Number
},
count: {
type: String
}
},
computed: {
pgConfig() {
return {
route: this.route,
apiRoot: this.apiRoot,
token: this.token,
query: this.query,
single: this.single,
limit: this.limit,
offset: this.offset,
count: this.count
};
}
},
onError(err) {
this.$emit("error", err);
},
render(h) {
return this.$scopedSlots.default(this.pg);
}
};
class Route extends Function {
constructor(request2, ready) {
super("", "return arguments.callee.request.apply(arguments.callee, arguments)");
this.request = request2;
this.options = request2.bind(null, "OPTIONS");
this.get = request2.bind(null, "GET");
this.head = request2.bind(null, "HEAD");
this.post = request2.bind(null, "POST");
this.put = request2.bind(null, "PUT");
this.patch = request2.bind(null, "PATCH");
this.delete = request2.bind(null, "DELETE");
Object.defineProperty(this, "$ready", {
value: ready
});
}
_extractFromDefinition(tableDef) {
this.columns = Object.keys(tableDef.properties);
this.pks = Object.entries(tableDef.properties).filter(([field, fieldDef]) => fieldDef.description?.includes("<pk/>")).map(([field]) => field);
}
}
class RPC extends Function {
constructor(request2, ready) {
super("", "return arguments.callee._call.apply(arguments.callee, arguments)");
this._request = request2;
Object.defineProperty(this, "$ready", {
value: ready
});
}
async _call(fn, signal, params, opts) {
if (!(signal instanceof AbortSignal)) {
opts = params;
params = signal;
signal = void 0;
}
const { get, query, ...requestOptions } = opts ?? {};
if (get) {
return this._request("rpc/" + fn, "GET", Object.assign({}, query, params), { ...requestOptions, signal });
} else {
return this._request("rpc/" + fn, "POST", query, { ...requestOptions, signal }, params);
}
}
}
function isLogicalOperator(k) {
return ["and", "or", "not.and", "not.or"].includes(k);
}
function parseKey(key) {
if (!key.includes(".")) return [key];
const parts = key.split(".");
const operator = parts.at(-1);
const not = parts.at(-2) === "not";
const field = parts.slice(0, not ? -2 : -1).join(".");
if (not) return [field, "not", operator];
return [field, operator];
}
function quoteValue(str) {
str = str.toString();
if ([",", ".", ":", "(", ")"].find((r) => str.includes(r)) || ["null", "true", "false"].includes(str)) {
return `"${str}"`;
} else {
return str;
}
}
function cc(prefix, str, suffix = "") {
str = str?.toString();
return str ? `${prefix}${str}${suffix}` : "";
}
class Query extends URL {
subQueries = {};
#apiRoot;
constructor(apiRoot, route, queryObject = {}) {
const url = apiRoot.replace(/\/$/, "") + "/" + route.replace(/^\//, "");
super(url, window.location.href);
this.#apiRoot = apiRoot;
const { columns, select, order, limit, offset, on_conflict, ...conditions } = queryObject;
if (on_conflict) this.searchParams.append("on_conflict", on_conflict);
if (columns) this.searchParams.append("columns", columns);
this._appendSelect(select);
this._appendOrder(order);
this._appendLimit(limit);
this._appendOffset(offset);
this._appendConditions(conditions);
this._appendSubQueryParams(this);
}
_appendSubQueryParams(parent, aliasChain = "") {
for (let [alias, query] of Object.entries(parent.subQueries)) {
alias = cc(`${aliasChain}`, alias);
for (const [key, value] of query.searchParams.entries()) {
if (["columns", "select"].includes(key)) continue;
this.searchParams.append(`${alias}.${key}`, value);
}
this._appendSubQueryParams(query, alias);
}
}
_appendSelect(select) {
if (typeof select === "object" && !Array.isArray(select)) {
this.searchParams.append("select", this._parseSelectObject(select));
} else if (select) {
this.searchParams.append("select", select.toString());
}
}
_parseSelectObject(obj, jsonChain = []) {
return Object.entries(obj).map(([k, v]) => {
if (!v) return false;
if (v?.select) {
const alias2 = k.split(":", 1)[0].split("!", 1)[0];
const subQuery = new Query(this.#apiRoot, alias2, v);
this.subQueries[alias2] = subQuery;
return `${k}(${subQuery.searchParams.get("select")})`;
}
let alias = "";
let field;
let cast = "";
let subfields = [];
if (/^[^"]*:/.test(k)) {
[alias, field] = k.split(/:(.+)/);
} else if (/^".*[^\\]":/.test(k)) {
[alias, field] = k.split(new RegExp('(?<=[^\\\\]"):(.+)'));
} else {
field = k;
}
if (typeof v === "string") {
cast = v;
} else if (typeof v === "object") {
let fields;
({ "::": cast, ...fields } = v);
subfields = this._parseSelectObject(fields, [...jsonChain, field]);
if (subfields.length > 0 && !alias && !cast) {
return subfields;
}
}
return [
cc("", alias, ":") + [...jsonChain, field].join("->") + cc("::", cast),
subfields
];
}).flat(2).filter(Boolean).join(",");
}
_appendOrder(order) {
if (Array.isArray(order)) {
this.searchParams.append("order", order.map((item) => {
if (Array.isArray(item)) {
return item.join(".");
} else {
return item;
}
}).join(","));
} else if (typeof order === "object") {
this.searchParams.append("order", Object.entries(order).map(([k, v]) => {
if (v && typeof v === "string") {
return `${k}.${v}`;
} else {
return k;
}
}).join(","));
} else if (order) {
this.searchParams.append("order", order);
}
}
_appendLimit(limit) {
if (limit) {
this.searchParams.append("limit", limit);
}
}
_appendOffset(offset) {
if (offset) {
this.searchParams.append("offset", offset);
}
}
_appendConditions(obj) {
for (const { key, value } of this._parseConditions(obj)) {
this.searchParams.append(key, value);
}
}
_parseConditions(obj, jsonPrefix = "", quoteStrings = false) {
return Object.entries(obj).map(([key, value]) => {
if (value === void 0) return false;
const aliasKey = key.split(":");
key = aliasKey[1] ?? aliasKey[0];
if (isLogicalOperator(key)) {
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("no object for logical operator");
if (jsonPrefix) throw new Error("logical operators can't be nested with json operators");
const strValue = this._parseConditions(value, "", true).map(({ key: k, value: v }) => {
return isLogicalOperator(k) ? `${k}${v}` : `${k}.${v}`;
}).join(",");
if (!strValue) return void 0;
return {
key,
value: `(${strValue})`
};
} else {
const [field, ...ops] = parseKey(key);
let strValue;
switch (ops[ops.length - 1]) {
case "in":
strValue = this._valueToString(value, "()", true);
break;
case void 0:
if (value && typeof value === "object" && !Array.isArray(value)) {
return this._parseConditions(value, cc("", jsonPrefix, "->") + field);
}
// falls through
default:
strValue = this._valueToString(value, "{}", quoteStrings);
}
const jsonOperator = typeof value === "string" ? "->>" : "->";
return {
key: cc("", jsonPrefix, jsonOperator) + field,
value: [...ops, strValue].join(".")
};
}
}).flat().filter(Boolean);
}
_valueToString(value, arrayBrackets, quoteStrings) {
if (value === null) {
return "null";
} else if (typeof value === "boolean") {
return value.toString();
} else if (Array.isArray(value)) {
return arrayBrackets.charAt(0) + value.map((v) => this._valueToString(v, "{}", true)).join(",") + arrayBrackets.charAt(1);
} else if (typeof value === "object") {
const { lower, includeLower = true, upper, includeUpper = false } = value;
return (includeLower ? "[" : "(") + lower + "," + upper + (includeUpper ? "]" : ")");
} else {
return quoteStrings ? quoteValue(value) : value;
}
}
}
let defaultHeaders;
function setDefaultHeaders(headers) {
defaultHeaders = new Headers(headers);
}
const acceptHeaderMap = {
"": "application/json",
single: "application/vnd.pgrst.object+json",
binary: "application/octet-stream",
text: "text/plain"
};
async function request(apiRoot, token, route, method, query = {}, options = {}, body) {
const headers = new Headers(defaultHeaders);
const isJSONBody = ![
Blob,
FormData,
URLSearchParams,
// should implement ReadableStream here, but does not exist in node, so throws in tests
ArrayBuffer,
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array,
DataView,
String,
void 0
].includes(body?.constructor);
if (isJSONBody) {
headers.set("Content-Type", "application/json");
}
headers.set("Accept", acceptHeaderMap[options.accept ?? ""] || options.accept);
if (options.limit === 0) {
headers.set("Range-Unit", "items");
headers.set("Range", "-0");
} else if (options.limit || options.offset !== void 0) {
const lower = options.offset ?? 0;
const upper = options.limit ? lower + options.limit - 1 : "";
headers.set("Range-Unit", "items");
headers.set("Range", [lower, upper].join("-"));
}
const prefer = ["return", "count", "params", "resolution"].filter((key) => options[key]).map((key) => `${key}=${options[key]}`).join(",");
if (prefer) {
headers.append("Prefer", prefer);
}
if (token) {
headers.set("Authorization", `Bearer ${token}`);
}
if (options.headers) {
for (const [k, v] of Object.entries(options.headers)) {
headers.set(k, v);
}
}
const url = new Query(apiRoot, route, query);
return await fetch(url.toString(), {
method,
headers,
body: isJSONBody ? JSON.stringify(body) : body,
signal: options.signal
}).then(throwWhenStatusNotOk);
}
let schemaCache = {};
function resetSchemaCache() {
schemaCache = {};
}
let defaultApiRoot = "/";
let defaultToken;
function setDefaultRoot(apiRoot = defaultApiRoot) {
defaultApiRoot = apiRoot;
}
function setDefaultToken(token) {
defaultToken = token;
}
class Schema extends Function {
#apiRoot;
#token;
constructor(apiRoot = defaultApiRoot, token = defaultToken) {
super("", "return arguments.callee._call.apply(arguments.callee, arguments)");
const cached = schemaCache[apiRoot] && schemaCache[apiRoot][token];
if (cached) return cached;
this.#apiRoot = apiRoot;
this.#token = token;
if (!schemaCache[apiRoot]) {
schemaCache[apiRoot] = {};
}
schemaCache[apiRoot][token] = this;
const ready = new Promise(async (resolve, reject) => {
try {
const schema = await this._fetchSchema(apiRoot, token);
for (const path of Object.keys(schema.paths ?? {})) {
if (path.startsWith("/rpc/")) {
const fn = path.substring(5);
this.rpc[fn] = new ObservableFunction(this.rpc.bind(this.rpc, fn));
} else {
const route = path.substring(1);
this._createRoute(route);
}
}
for (const [route, def] of Object.entries(schema.definitions ?? {})) {
this._createRoute(route, def);
}
resolve();
} catch (e) {
reject(e);
}
});
Object.defineProperty(this, "$ready", {
value: ready
});
this.rpc = new RPC(request.bind(null, this.#apiRoot, this.#token), this.$ready);
}
_call(apiRoot = this.#apiRoot, token = this.#token) {
return new Schema(apiRoot, token);
}
$route(route) {
return this._createRoute(route);
}
_createRoute(route, def) {
if (!this[route]) {
this[route] = new Route(request.bind(null, this.#apiRoot, this.#token, route), this.$ready);
}
if (def) {
this[route]._extractFromDefinition(def);
}
return this[route];
}
async _fetchSchema(apiRoot, token) {
const headers = new Headers();
if (token) {
headers.set("Authorization", `Bearer ${token}`);
}
try {
const url = new URL(apiRoot, window.location.href);
const resp = await fetch(url.toString(), { headers }).then(throwWhenStatusNotOk);
const body = await resp.json();
if (!resp.headers.get("Content-Type").startsWith("application/openapi+json")) {
throw new Error("wrong body format");
}
return body;
} catch (err) {
throw new SchemaNotFoundError(apiRoot, err);
}
}
}
function usePostgrest(...args) {
return new Schema(...args);
}
const Plugin = {
install(Vue2, options = {}) {
Vue2.config.optionMergeStrategies.onError = Vue2.config.optionMergeStrategies.created;
Vue2.component("postgrest", Postgrest);
Object.defineProperty(Vue2.prototype, "$postgrest", {
get: usePostgrest
});
setDefaultRoot(options.apiRoot);
setDefaultHeaders(options.headers);
}
};
export {
AuthError,
FetchError,
PrimaryKeyError,
SchemaNotFoundError,
Plugin as default,
mixin as pg,
resetSchemaCache,
setDefaultToken,
usePostgrest
};
//# sourceMappingURL=vue-postgrest.js.map