@onreza/sqlx-js 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ import { createHash } from "node:crypto";
2
+ import { isEscapeStringPrefix, isIdentifierContinuation, readBlockComment, readDollarQuoted, readLineComment, readQuotedIdentifier, readSingleQuoted, } from "./sql-lex.js";
3
+ export function queryId(query) {
4
+ return createHash("sha256").update(normalizeQuery(query)).digest("hex").slice(0, 16);
5
+ }
6
+ function normalizeQuery(query) {
7
+ let out = "";
8
+ let pendingSpace = false;
9
+ let i = 0;
10
+ const emit = (text) => {
11
+ if (pendingSpace && out.length > 0)
12
+ out += " ";
13
+ out += text;
14
+ pendingSpace = false;
15
+ };
16
+ const markSpace = () => {
17
+ if (out.length > 0)
18
+ pendingSpace = true;
19
+ };
20
+ while (i < query.length) {
21
+ const ch = query[i];
22
+ if (/\s/.test(ch)) {
23
+ markSpace();
24
+ i++;
25
+ continue;
26
+ }
27
+ if (ch === "-" && query[i + 1] === "-") {
28
+ i = readLineComment(query, i);
29
+ markSpace();
30
+ continue;
31
+ }
32
+ if (ch === "/" && query[i + 1] === "*") {
33
+ i = readBlockComment(query, i);
34
+ markSpace();
35
+ continue;
36
+ }
37
+ if (ch === "'") {
38
+ const next = readSingleQuoted(query, i, isEscapeStringPrefix(query, i));
39
+ emit(query.slice(i, next));
40
+ i = next;
41
+ continue;
42
+ }
43
+ if (ch === "\"") {
44
+ const next = readQuotedIdentifier(query, i);
45
+ emit(query.slice(i, next));
46
+ i = next;
47
+ continue;
48
+ }
49
+ if (ch === "$") {
50
+ const next = i === 0 || !isIdentifierContinuation(query[i - 1]) ? readDollarQuoted(query, i) : null;
51
+ if (next !== null) {
52
+ emit(query.slice(i, next));
53
+ i = next;
54
+ continue;
55
+ }
56
+ }
57
+ emit(ch);
58
+ i++;
59
+ }
60
+ return out;
61
+ }
@@ -0,0 +1,62 @@
1
+ import type { ExecuteResult } from "./runtime.js";
2
+ import type { TypedSqlForRegistry } from "./typed.js";
3
+ export type QueryExecutionMode = "many" | "one" | "optional" | "execute";
4
+ export type QueryExecutionMetadata = {
5
+ queryId: string;
6
+ queryName?: string;
7
+ };
8
+ export declare const QUERY_EXECUTOR: unique symbol;
9
+ export type QueryExecutorMethod = (mode: QueryExecutionMode, query: string, params: unknown[], metadata: QueryExecutionMetadata) => Promise<unknown>;
10
+ type NamedQueryEntry = {
11
+ params: Record<string, unknown>;
12
+ row: unknown;
13
+ };
14
+ type PositionalQueryEntry = {
15
+ params: readonly unknown[];
16
+ row: unknown;
17
+ };
18
+ type QueryModeResult<Mode extends QueryExecutionMode, Row> = Mode extends "many" ? Row[] : Mode extends "one" ? Row : Mode extends "optional" ? Row | null : ExecuteResult;
19
+ export type QueryDefinition<Query extends string = string, Mode extends QueryExecutionMode = QueryExecutionMode> = {
20
+ readonly query: Query;
21
+ readonly mode: Mode;
22
+ readonly queryId: string;
23
+ readonly queryName?: string;
24
+ run<Registry extends {
25
+ queries: Record<Query, NamedQueryEntry>;
26
+ fileQueries: object;
27
+ }>(executor: TypedSqlForRegistry<Registry>, params: RegistryParams<Query, Registry>): Promise<QueryResultFor<QueryDefinition<Query, Mode>, Registry>>;
28
+ run<Registry extends {
29
+ queries: Record<Query, PositionalQueryEntry>;
30
+ fileQueries: object;
31
+ }>(executor: TypedSqlForRegistry<Registry>, ...params: RegistryParams<Query, Registry> & readonly unknown[]): Promise<QueryResultFor<QueryDefinition<Query, Mode>, Registry>>;
32
+ };
33
+ type DefinitionQuery<Definition> = Definition extends QueryDefinition<infer Query, QueryExecutionMode> ? Query : never;
34
+ type DefinitionMode<Definition> = Definition extends QueryDefinition<string, infer Mode> ? Mode : never;
35
+ type RegistryQuery<Query extends string, Registry extends {
36
+ queries: object;
37
+ }> = Registry["queries"][Query & keyof Registry["queries"]];
38
+ type RegistryParams<Query extends string, Registry extends {
39
+ queries: object;
40
+ }> = RegistryQuery<Query, Registry>["params" & keyof RegistryQuery<Query, Registry>];
41
+ type RegistryRow<Query extends string, Registry extends {
42
+ queries: object;
43
+ }> = RegistryQuery<Query, Registry>["row" & keyof RegistryQuery<Query, Registry>];
44
+ export type QueryParamsFor<Definition, Registry extends {
45
+ queries: object;
46
+ }> = RegistryParams<DefinitionQuery<Definition>, Registry>;
47
+ export type QueryRowFor<Definition, Registry extends {
48
+ queries: object;
49
+ }> = RegistryRow<DefinitionQuery<Definition>, Registry>;
50
+ export type QueryResultFor<Definition, Registry extends {
51
+ queries: object;
52
+ }> = QueryModeResult<DefinitionMode<Definition>, QueryRowFor<Definition, Registry>>;
53
+ type DefineQueryMethod<Mode extends QueryExecutionMode> = {
54
+ <const Query extends string>(query: Query): QueryDefinition<Query, Mode>;
55
+ <const Query extends string>(name: string, query: Query): QueryDefinition<Query, Mode>;
56
+ };
57
+ export declare const defineQuery: DefineQueryMethod<"many"> & {
58
+ one: DefineQueryMethod<"one">;
59
+ optional: DefineQueryMethod<"optional">;
60
+ execute: DefineQueryMethod<"execute">;
61
+ };
62
+ export {};
@@ -0,0 +1,38 @@
1
+ import { queryId } from "./query-id.js";
2
+ export const QUERY_EXECUTOR = Symbol.for("@onreza/sqlx-js.query-executor");
3
+ function definitionMethod(mode) {
4
+ return ((nameOrQuery, maybeQuery) => {
5
+ const query = maybeQuery ?? nameOrQuery;
6
+ const name = maybeQuery === undefined ? undefined : nameOrQuery;
7
+ if (name !== undefined && name.trim() === "") {
8
+ throw new Error("sqlx-js.defineQuery: query name must not be empty");
9
+ }
10
+ const metadata = {
11
+ queryId: queryId(query),
12
+ ...(name ? { queryName: name } : {}),
13
+ };
14
+ return Object.freeze({
15
+ query,
16
+ mode,
17
+ queryId: metadata.queryId,
18
+ ...(name ? { queryName: name } : {}),
19
+ async run(executor, ...params) {
20
+ const execute = executor[QUERY_EXECUTOR];
21
+ if (execute)
22
+ return await execute(mode, query, params, metadata);
23
+ if (mode === "one")
24
+ return await executor.one(query, ...params);
25
+ if (mode === "optional")
26
+ return await executor.optional(query, ...params);
27
+ if (mode === "execute")
28
+ return await executor.execute(query, ...params);
29
+ return await executor(query, ...params);
30
+ },
31
+ });
32
+ });
33
+ }
34
+ export const defineQuery = Object.assign(definitionMethod("many"), {
35
+ one: definitionMethod("one"),
36
+ optional: definitionMethod("optional"),
37
+ execute: definitionMethod("execute"),
38
+ });
@@ -1,5 +1,8 @@
1
1
  import { PgError } from "./pg/wire.js";
2
+ import { QUERY_EXECUTOR, type QueryExecutorMethod } from "./query.js";
2
3
  export type OnQueryEvent = {
4
+ queryId: string;
5
+ queryName?: string;
3
6
  query: string;
4
7
  params: unknown[];
5
8
  durationMs: number;
@@ -21,6 +24,7 @@ export type RuntimeClient = {
21
24
  onQueryHookError?: OnQueryHookError;
22
25
  fileRoot?: string;
23
26
  reloadSqlFiles?: boolean;
27
+ sqlFiles?: Readonly<Record<string, string>>;
24
28
  };
25
29
  type AnyFn = (...args: unknown[]) => Promise<unknown[]>;
26
30
  type AnyOneFn = (...args: unknown[]) => Promise<unknown>;
@@ -28,7 +32,7 @@ type AnyOptionalFn = (...args: unknown[]) => Promise<unknown | null>;
28
32
  type AnyExecuteFn = (...args: unknown[]) => Promise<ExecuteResult>;
29
33
  type IdentifierFn = (...parts: string[]) => string;
30
34
  declare const PARAMETER_KIND: unique symbol;
31
- export type JsonParameter<T = JsonInputValue> = {
35
+ export type JsonParameter<T = unknown> = {
32
36
  readonly [PARAMETER_KIND]: "json";
33
37
  readonly value: T;
34
38
  };
@@ -42,11 +46,17 @@ export type JsonInputObject = {
42
46
  readonly [key: string]: JsonInputValue | undefined;
43
47
  };
44
48
  export type JsonInputArray = readonly JsonInputValue[];
49
+ type NonJsonValue = bigint | symbol | Date | Uint8Array | ((...args: never[]) => unknown);
50
+ export type JsonCompatible<T> = T extends JsonPrimitive ? T : T extends NonJsonValue ? never : T extends readonly unknown[] ? T extends JsonInputArray ? T : {
51
+ readonly [K in keyof T]: JsonCompatible<T[K]>;
52
+ } : T extends object ? Extract<keyof T, symbol> extends never ? T extends JsonInputObject ? T : {
53
+ readonly [K in keyof T]: undefined extends T[K] ? JsonCompatible<Exclude<T[K], undefined>> | undefined : JsonCompatible<T[K]>;
54
+ } : never : never;
45
55
  export type ExecuteResult = {
46
56
  rowCount: number;
47
57
  command: string;
48
58
  };
49
- export declare function json<T extends JsonInputValue>(value: T): JsonParameter<T>;
59
+ export declare function json<T>(value: T & JsonCompatible<T>): JsonParameter<T>;
50
60
  export declare function array<T>(value: readonly (T | null)[]): PgArrayParameter<T>;
51
61
  export declare function parameterKind(value: unknown): "json" | "array" | undefined;
52
62
  declare function renameRows(rows: unknown[]): unknown[];
@@ -63,6 +73,19 @@ export declare class TooManyRowsError extends Error {
63
73
  constructor(actual: number, expected?: "1" | "0 or 1");
64
74
  }
65
75
  export declare function toPgError(e: unknown): PgError | null;
76
+ export declare const SQLSTATE: {
77
+ readonly notNullViolation: "23502";
78
+ readonly foreignKeyViolation: "23503";
79
+ readonly uniqueViolation: "23505";
80
+ readonly checkViolation: "23514";
81
+ readonly serializationFailure: "40001";
82
+ readonly deadlockDetected: "40P01";
83
+ };
84
+ export type KnownSqlState = (typeof SQLSTATE)[keyof typeof SQLSTATE];
85
+ export declare function isPgError(error: unknown): error is PgError;
86
+ export declare function isPgError<const Code extends string>(error: unknown, code: Code): error is PgError & {
87
+ readonly code: Code;
88
+ };
66
89
  export declare const _internal: {
67
90
  renameRows: typeof renameRows;
68
91
  encodeParam: typeof encodeParam;
@@ -74,7 +97,7 @@ export declare const _internal: {
74
97
  parameterKind: typeof parameterKind;
75
98
  toPgError: typeof toPgError;
76
99
  };
77
- declare function loadSqlFile(path: string, fileRoot?: string, reload?: boolean): string;
100
+ declare function loadSqlFile(path: string, fileRoot?: string, reload?: boolean, embedded?: Readonly<Record<string, string>>): string;
78
101
  export declare function clearSqlFileCache(): void;
79
102
  declare function clearIdentifierCache(): void;
80
103
  export declare function id(...parts: string[]): string;
@@ -91,6 +114,7 @@ type SqlCallable = AnyFn & {
91
114
  id: IdentifierFn;
92
115
  json: typeof json;
93
116
  array: typeof array;
117
+ [QUERY_EXECUTOR]: QueryExecutorMethod;
94
118
  };
95
119
  export type TransactionOptions = {
96
120
  isolation?: "read uncommitted" | "read committed" | "repeatable read" | "serializable";
@@ -3,6 +3,8 @@ import { isAbsolute, relative, resolve } from "node:path";
3
3
  import { PgClient, parseDatabaseUrl, PgError } from "./pg/wire.js";
4
4
  import { applyPending, acquireMigrateLock, releaseMigrateLock, DEFAULT_MIGRATE_LOCK_KEY } from "./migration-core.js";
5
5
  import { bindNamedParameters, rewriteNamedParameters } from "./sql-params.js";
6
+ import { queryId } from "./query-id.js";
7
+ import { QUERY_EXECUTOR, } from "./query.js";
6
8
  const PARAMETER_KIND = Symbol("sqlx-js.parameter");
7
9
  export function json(value) {
8
10
  return { [PARAMETER_KIND]: "json", value };
@@ -176,7 +178,7 @@ export class TooManyRowsError extends Error {
176
178
  }
177
179
  // SQLSTATE is exactly five characters from [0-9A-Z]; lowercase or other shapes
178
180
  // are never valid, so transport codes like "EPIPE" must not match on shape alone.
179
- const SQLSTATE = /^[0-9A-Z]{5}$/;
181
+ const SQLSTATE_PATTERN = /^[0-9A-Z]{5}$/;
180
182
  function firstString(...candidates) {
181
183
  for (const value of candidates) {
182
184
  if (typeof value === "string" && value.length > 0)
@@ -200,7 +202,7 @@ export function toPgError(e) {
200
202
  // and system errors (EPIPE, ECONNREFUSED, CONNECTION_ENDED) carry neither, so
201
203
  // they pass through untouched instead of masquerading as a PgError.
202
204
  const isDatabaseError = o.name === "PostgresError" ||
203
- (code !== undefined && SQLSTATE.test(code) && severity !== undefined);
205
+ (code !== undefined && SQLSTATE_PATTERN.test(code) && severity !== undefined);
204
206
  if (!isDatabaseError)
205
207
  return null;
206
208
  const fields = {};
@@ -231,6 +233,17 @@ export function toPgError(e) {
231
233
  fields.s = schema;
232
234
  return new PgError(fields, { cause: e });
233
235
  }
236
+ export const SQLSTATE = {
237
+ notNullViolation: "23502",
238
+ foreignKeyViolation: "23503",
239
+ uniqueViolation: "23505",
240
+ checkViolation: "23514",
241
+ serializationFailure: "40001",
242
+ deadlockDetected: "40P01",
243
+ };
244
+ export function isPgError(error, code) {
245
+ return error instanceof PgError && (code === undefined || error.code === code);
246
+ }
234
247
  export const _internal = {
235
248
  renameRows,
236
249
  encodeParam,
@@ -242,7 +255,21 @@ export const _internal = {
242
255
  parameterKind,
243
256
  toPgError,
244
257
  };
245
- async function runRawQuery(client, query, params) {
258
+ const runtimeQueryIds = new Map();
259
+ const MAX_RUNTIME_QUERY_IDS = 1024;
260
+ function observedMetadata(query, metadata) {
261
+ if (metadata)
262
+ return metadata;
263
+ let id = runtimeQueryIds.get(query);
264
+ if (!id) {
265
+ id = queryId(query);
266
+ if (runtimeQueryIds.size >= MAX_RUNTIME_QUERY_IDS)
267
+ runtimeQueryIds.clear();
268
+ runtimeQueryIds.set(query, id);
269
+ }
270
+ return { queryId: id };
271
+ }
272
+ async function runRawQuery(client, query, params, metadata) {
246
273
  const observedQuery = query;
247
274
  const observedParams = params;
248
275
  const bound = bindNamedParameters(rewriteNamedParameters(query), params);
@@ -260,10 +287,12 @@ async function runRawQuery(client, query, params) {
260
287
  throw toPgError(e) ?? e;
261
288
  }
262
289
  }
290
+ const observed = observedMetadata(observedQuery, metadata);
263
291
  const start = performance.now();
264
292
  try {
265
293
  const result = await client.query(query, encoded);
266
294
  notifyQuery(client, {
295
+ ...observed,
267
296
  query: observedQuery,
268
297
  params: observedParams,
269
298
  durationMs: performance.now() - start,
@@ -273,7 +302,7 @@ async function runRawQuery(client, query, params) {
273
302
  }
274
303
  catch (e) {
275
304
  const error = toPgError(e) ?? e;
276
- notifyQuery(client, { query: observedQuery, params: observedParams, durationMs: performance.now() - start, error });
305
+ notifyQuery(client, { ...observed, query: observedQuery, params: observedParams, durationMs: performance.now() - start, error });
277
306
  throw error;
278
307
  }
279
308
  }
@@ -296,26 +325,26 @@ function notifyQueryHookError(client, error, event) {
296
325
  catch {
297
326
  }
298
327
  }
299
- async function runQuery(client, query, params) {
300
- return renameRows(await runRawQuery(client, query, params));
328
+ async function runQuery(client, query, params, metadata) {
329
+ return renameRows(await runRawQuery(client, query, params, metadata));
301
330
  }
302
- async function runExecute(client, query, params) {
303
- const result = await runRawQuery(client, query, params);
331
+ async function runExecute(client, query, params, metadata) {
332
+ const result = await runRawQuery(client, query, params, metadata);
304
333
  return {
305
334
  rowCount: result.count ?? result.length,
306
335
  command: result.command ?? "",
307
336
  };
308
337
  }
309
- async function runOne(client, query, params) {
310
- const rows = await runQuery(client, query, params);
338
+ async function runOne(client, query, params, metadata) {
339
+ const rows = await runQuery(client, query, params, metadata);
311
340
  if (rows.length === 1)
312
341
  return rows[0];
313
342
  if (rows.length === 0)
314
343
  throw new NoRowsError();
315
344
  throw new TooManyRowsError(rows.length, "1");
316
345
  }
317
- async function runOptional(client, query, params) {
318
- const rows = await runQuery(client, query, params);
346
+ async function runOptional(client, query, params, metadata) {
347
+ const rows = await runQuery(client, query, params, metadata);
319
348
  if (rows.length === 0)
320
349
  return null;
321
350
  if (rows.length === 1)
@@ -323,7 +352,7 @@ async function runOptional(client, query, params) {
323
352
  throw new TooManyRowsError(rows.length, "0 or 1");
324
353
  }
325
354
  const sqlFileCache = new Map();
326
- function loadSqlFile(path, fileRoot = process.env.SQLX_JS_FILE_ROOT ?? process.cwd(), reload = false) {
355
+ function loadSqlFile(path, fileRoot = process.env.SQLX_JS_FILE_ROOT ?? process.cwd(), reload = false, embedded) {
327
356
  const root = resolve(fileRoot);
328
357
  if (isAbsolute(path)) {
329
358
  throw new Error(`sqlx-js.sql.file: path must be relative to fileRoot: ${path}`);
@@ -333,6 +362,8 @@ function loadSqlFile(path, fileRoot = process.env.SQLX_JS_FILE_ROOT ?? process.c
333
362
  if (rel === ".." || rel.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) || isAbsolute(rel)) {
334
363
  throw new Error(`sqlx-js.sql.file: path escapes fileRoot: ${path}`);
335
364
  }
365
+ if (embedded && Object.hasOwn(embedded, path))
366
+ return embedded[path];
336
367
  try {
337
368
  const cached = sqlFileCache.get(full);
338
369
  if (cached && !reload)
@@ -464,21 +495,30 @@ export function id(...parts) {
464
495
  }
465
496
  return parts.map(quoteIdentifier).join(".");
466
497
  }
498
+ function executeDefinedQuery(client, mode, query, params, metadata) {
499
+ if (mode === "one")
500
+ return runOne(client, query, params, metadata);
501
+ if (mode === "optional")
502
+ return runOptional(client, query, params, metadata);
503
+ if (mode === "execute")
504
+ return runExecute(client, query, params, metadata);
505
+ return runQuery(client, query, params, metadata);
506
+ }
467
507
  function makeBoundCallable(client) {
468
508
  const fn = (async (query, ...params) => {
469
509
  return runQuery(client, query, params);
470
510
  });
471
511
  const file = (async (path, ...params) => {
472
- return runQuery(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
512
+ return runQuery(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
473
513
  });
474
514
  file.one = (async (path, ...params) => {
475
- return runOne(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
515
+ return runOne(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
476
516
  });
477
517
  file.optional = (async (path, ...params) => {
478
- return runOptional(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
518
+ return runOptional(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
479
519
  });
480
520
  file.execute = (async (path, ...params) => {
481
- return runExecute(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
521
+ return runExecute(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
482
522
  });
483
523
  fn.file = file;
484
524
  fn.one = (async (query, ...params) => {
@@ -493,6 +533,9 @@ function makeBoundCallable(client) {
493
533
  fn.id = id;
494
534
  fn.json = json;
495
535
  fn.array = array;
536
+ fn[QUERY_EXECUTOR] = (mode, query, params, metadata) => {
537
+ return executeDefinedQuery(client, mode, query, params, metadata);
538
+ };
496
539
  return fn;
497
540
  }
498
541
  function buildSetTransaction(opts) {
@@ -513,19 +556,19 @@ export function createSqlRuntime(getClient) {
513
556
  });
514
557
  const rootFile = (async (path, ...params) => {
515
558
  const client = getClient();
516
- return runQuery(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
559
+ return runQuery(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
517
560
  });
518
561
  rootFile.one = (async (path, ...params) => {
519
562
  const client = getClient();
520
- return runOne(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
563
+ return runOne(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
521
564
  });
522
565
  rootFile.optional = (async (path, ...params) => {
523
566
  const client = getClient();
524
- return runOptional(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
567
+ return runOptional(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
525
568
  });
526
569
  rootFile.execute = (async (path, ...params) => {
527
570
  const client = getClient();
528
- return runExecute(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles), params);
571
+ return runExecute(client, loadSqlFile(path, client.fileRoot, client.reloadSqlFiles, client.sqlFiles), params);
529
572
  });
530
573
  root.file = rootFile;
531
574
  root.one = (async (query, ...params) => {
@@ -540,6 +583,9 @@ export function createSqlRuntime(getClient) {
540
583
  root.id = id;
541
584
  root.json = json;
542
585
  root.array = array;
586
+ root[QUERY_EXECUTOR] = (mode, query, params, metadata) => {
587
+ return executeDefinedQuery(getClient(), mode, query, params, metadata);
588
+ };
543
589
  root.transaction = (async (fnOrOpts, maybeFn) => {
544
590
  const c = getClient();
545
591
  let opts = {};
@@ -6,6 +6,8 @@ export type QueryCallSite = {
6
6
  query: string;
7
7
  paramCount: number;
8
8
  kind: "inline" | "file";
9
+ cardinality?: "many" | "one" | "optional" | "execute";
10
+ queryName?: string;
9
11
  sqlFilePath?: string;
10
12
  };
11
13
  export declare class ScanError extends Error {