UNPKG

@pgxsinkit/pgwasm

Version:
202 lines (170 loc) • 7.64 kB
// Began as a copy of `@electric-sql/pglite` (taken under its PostgreSQL License option, © ElectricSQL // — see NOTICE): `makeQueryError` began as its `makePGliteError`. Owned outright (ADR-0062); // compatibility with PGlite is an anti-goal — evolve freely. import type { BuildIdentity, FilesystemKind } from "./build/seam"; import type { QueryOptions } from "./interface"; import type { DatabaseError } from "./protocol/wire/messages"; /** The base of every error pgwasm itself raises (SQL errors are {@link DatabaseError}). */ export class PgwasmError extends Error { override name = "PgwasmError"; } /** A `dataDir` pgwasm does not accept: an unknown scheme, a bare path, or `dataDir` with `fs`. */ export class UnsupportedDataDirError extends PgwasmError { override name = "UnsupportedDataDirError"; readonly dataDir: string; constructor(dataDir: string, reason: string) { super(`Unsupported dataDir "${dataDir}": ${reason}`); this.dataDir = dataDir; } } /** An `opfs-ahp://` dataDir: that filesystem is gone (ADR-0062 decision 6). */ export class OpfsAhpRemovedError extends UnsupportedDataDirError { override name = "OpfsAhpRemovedError"; /** Where the OPFS store lives now. */ readonly replacement = "@pgxsinkit/pgwasm/opfs"; constructor(dataDir: string) { super( dataDir, "the opfs-ahp filesystem has been removed; the OPFS store is @pgxsinkit/pgwasm/opfs, passed in " + "explicitly as the `fs` option", ); } } /** The build does not mount the requested kind of storage. */ export class UnsupportedFilesystemError extends PgwasmError { override name = "UnsupportedFilesystemError"; readonly kind: FilesystemKind; readonly build: string; constructor(kind: FilesystemKind, build: BuildIdentity, supported: readonly FilesystemKind[]) { super( `The "${build.name}" Postgres build does not support ${kind} storage (it supports: ${supported.join(", ")}).`, ); this.kind = kind; this.build = build.name; } } /** The build needs a cross-origin-isolated context, and this one is not. */ export class CrossOriginIsolationRequiredError extends PgwasmError { override name = "CrossOriginIsolationRequiredError"; constructor(build: BuildIdentity) { super( `The "${build.name}" Postgres build needs a cross-origin-isolated context (SharedArrayBuffer); serve the ` + "page with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.", ); } } /** A server extension compiled for another build. */ export class ExtensionBuildMismatchError extends PgwasmError { override name = "ExtensionBuildMismatchError"; constructor(extension: string, extensionBuild: string, build: BuildIdentity) { super( `The "${extension}" extension was compiled for the "${extensionBuild}" Postgres build and cannot load into ` + `the "${build.name}" build.`, ); } } /** The build a data directory (or a Store backup) records. `"unmarked"` predates build markers. */ export type RecordedBuild = { readonly build: string; readonly dataFormat: number } | "unmarked"; function describeRecorded(recorded: RecordedBuild): string { return recorded === "unmarked" ? "an unmarked data directory (created before builds were recorded, by the C build)" : `the "${recorded.build}" Postgres build (data format ${recorded.dataFormat})`; } /** * A data directory or Store backup made by another Postgres build. A store's build is fixed when it is * created (ADR-0063 build permanence); nothing was written. */ export class BuildMismatchError extends PgwasmError { override name = "BuildMismatchError"; readonly expected: { readonly build: string; readonly dataFormat: number }; readonly found: RecordedBuild; readonly source: "data-directory" | "backup"; constructor(build: BuildIdentity, found: RecordedBuild, source: "data-directory" | "backup") { const what = source === "backup" ? "This Store backup" : "This data directory"; super( `${what} was created by ${describeRecorded(found)} and cannot be opened with the "${build.name}" build. A ` + "store's Postgres build is fixed when it is created: open it with that build, or destroy the store and " + "re-sync it.", ); this.expected = { build: build.name, dataFormat: build.dataFormat }; this.found = found; this.source = source; } } /** A data directory or backup of the same build in a data format this release does not read. */ export class DataFormatMismatchError extends PgwasmError { override name = "DataFormatMismatchError"; readonly expected: number; readonly found: number; readonly source: "data-directory" | "backup"; constructor(build: BuildIdentity, found: number, source: "data-directory" | "backup") { super( `This ${source === "backup" ? "Store backup" : "data directory"} is in data format ${found} of the ` + `"${build.name}" Postgres build; this release of the build reads format ${build.dataFormat}.`, ); this.expected = build.dataFormat; this.found = found; this.source = source; } } /** A build marker that cannot be read: corrupt, or written by a newer pgwasm. Nothing was written. */ export class BuildMarkerUnreadableError extends PgwasmError { override name = "BuildMarkerUnreadableError"; readonly raw: string; constructor(raw: string, reason: string) { super(`The data directory's build marker cannot be read (${reason}): ${JSON.stringify(raw)}`); this.raw = raw; } } /** `loadDataDir` was given for a data directory that already holds a database. */ export class DataDirExistsError extends PgwasmError { override name = "DataDirExistsError"; constructor() { super("The data directory already holds a database; a Store backup restores only into an empty one."); } } /** A Store backup that cannot be restored: not a tarball, or a member outside the data directory. */ export class BackupFormatError extends PgwasmError { override name = "BackupFormatError"; } /** The storage is held by another open database (an IndexedDB store open in another context). */ export class StorageInUseError extends PgwasmError { override name = "StorageInUseError"; constructor(message: string) { super(message); } } /** The database is closing or closed. */ export class PgwasmClosedError extends PgwasmError { override name = "PgwasmClosedError"; constructor(state: "closing" | "closed") { super(`pgwasm is ${state}`); } } /** * The Postgres build failed underneath a statement (an exception that is not an SQL error: a storage * failure thrown into the engine, a PANIC's abort). The instance is failed: every later statement * throws this same error, and `close()` releases resources and rejects with it. */ export class PgwasmFailedError extends PgwasmError { override name = "PgwasmFailedError"; } /** A feature the build does not have (see its capabilities). */ export class UnsupportedFeatureError extends PgwasmError { override name = "UnsupportedFeatureError"; } /** An SQL error from a query, with the query that raised it. */ export interface QueryError extends DatabaseError { query: string | undefined; params: readonly unknown[] | undefined; queryOptions: QueryOptions | undefined; } /** Attach the query to the SQL error it raised. */ export function makeQueryError(data: { error: DatabaseError; query: string; params: readonly unknown[] | undefined; options: QueryOptions | undefined; }): QueryError { return Object.assign(data.error, { query: data.query, params: data.params, queryOptions: data.options }); }