@incorta/sdk 1.8.0
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.
- package/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/index.cjs +1067 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +842 -0
- package/dist/index.d.ts +842 -0
- package/dist/index.js +1012 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/config.ts","../src/core/errors.ts","../src/core/enums.ts","../src/core/http.ts","../src/core/query.ts","../src/core/models.ts","../src/core/schemas.ts","../src/core/tables.ts","../src/core/user-client.ts","../src/index.ts"],"sourcesContent":["/**\n * Configuration resolution.\n *\n * Everything this SDK needs to address Incorta — the environment URL and the\n * tenant — is already part of an OAuth setup, so the configuration is the\n * `@incorta/auth` configuration plus two transport knobs. Pass an existing\n * {@link IncortaAuth} when the app already has one (the usual case: it is\n * mounted for login and gating), or pass the auth options and let this package\n * construct one.\n */\nimport { createIncortaAuth } from \"@incorta/auth\";\nimport type { IncortaAuth, IncortaAuthConfig } from \"@incorta/auth\";\n\nimport { IncortaConfigError } from \"./errors.js\";\n\n/**\n * Options for {@link createIncortaClient} — every `@incorta/auth` option, plus:\n *\n * - `auth`: an already-built {@link IncortaAuth} to reuse instead of creating\n * one. When given, the auth options are ignored (they live on that instance).\n * - `timeoutMs` / `maxRetries`: transport settings for the metadata calls.\n */\nexport type IncortaClientConfig = IncortaAuthConfig & {\n /**\n * An existing `@incorta/auth` instance to borrow sessions from. Prefer this\n * whenever the app already mounts one — a second instance would mean two\n * sets of OAuth state for the same users.\n */\n auth?: IncortaAuth;\n /**\n * Per-request timeout in milliseconds.\n * @default 30000\n */\n timeoutMs?: number;\n /**\n * Retries for transient failures (HTTP 429 and 5xx, and network errors).\n * Client errors are never retried — replaying them cannot help.\n * @default 3\n */\n maxRetries?: number;\n};\n\nexport interface ResolvedConfig {\n /** The auth instance whose sessions this client reads Incorta as. */\n auth: IncortaAuth;\n /**\n * The environment root used for these server-to-server calls. Honours the\n * auth SDK's split-horizon setting: `internalIncortaUrl` when set, otherwise\n * `incortaUrl`. Browser-facing URLs are unaffected.\n */\n baseUrl: string;\n tenant: string;\n /** `{baseUrl}/api/v2/{tenant}` — the root every metadata call hangs off. */\n apiRoot: string;\n timeoutMs: number;\n maxRetries: number;\n fetch: typeof fetch;\n}\n\nfunction stripTrailingSlash(url: string): string {\n return url.replace(/\\/+$/, \"\");\n}\n\n/**\n * Resolves configuration, building an {@link IncortaAuth} when one was not\n * supplied.\n *\n * @throws {IncortaConfigError} A transport option is malformed, or the auth\n * configuration is incomplete (missing `incortaUrl`, `tenant`, `clientId`,\n * `clientSecret`, or `secret` — as options or `INCORTA_*` environment\n * variables).\n */\nexport function resolveConfig(input: IncortaClientConfig = {}): ResolvedConfig {\n const { auth: provided, timeoutMs, maxRetries, ...authOptions } = input;\n\n const timeout = timeoutMs ?? 30_000;\n if (!Number.isFinite(timeout) || timeout <= 0) {\n throw new IncortaConfigError(\"timeoutMs must be a positive number of milliseconds.\");\n }\n\n const retries = maxRetries ?? 3;\n if (!Number.isInteger(retries) || retries < 0) {\n throw new IncortaConfigError(\"maxRetries must be an integer >= 0.\");\n }\n\n // Validate the transport options before touching auth, so an obvious typo is\n // not masked by a missing-credential error from the auth SDK.\n let auth: IncortaAuth;\n try {\n auth = provided ?? createIncortaAuth(authOptions);\n } catch (error) {\n throw new IncortaConfigError(\n `Could not configure OAuth: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n\n // Server-to-server: prefer the internal URL when the deployment sets one.\n const baseUrl = stripTrailingSlash(auth.config.internalIncortaUrl ?? auth.config.incortaUrl);\n const tenant = auth.config.tenant;\n\n return {\n auth,\n baseUrl,\n tenant,\n apiRoot: `${baseUrl}/api/v2/${tenant}`,\n timeoutMs: timeout,\n maxRetries: retries,\n // The same fetch the auth SDK uses for its own server-to-server calls, so a\n // proxy agent or test double installed there covers metadata calls too.\n fetch: auth.config.fetch,\n };\n}\n","/**\n * Error hierarchy for @incorta/sdk.\n *\n * Every failure thrown by this package derives from {@link IncortaError}, so\n * callers can catch one type and still inspect the Incorta error code when they\n * need detail.\n *\n * The Incorta API reports failures as `{\"message\": \"INC_09030108: Invalid ...\"}`.\n * The `INC_` prefix is a stable machine-readable code; {@link IncortaApiError.code}\n * exposes it separately from the human-readable remainder.\n */\n\n/** Matches the `INC_05022801: The [x] SCHEMADEFINITION cannot be found.` shape. */\nconst CODE_PATTERN = /^\\s*(INC_\\d+)\\s*:\\s*([\\s\\S]*)$/;\n\n/** Maximum characters of the raw body retained on an error. */\nconst BODY_LIMIT = 2000;\n\n/** Base class for every error thrown by @incorta/sdk. */\nexport class IncortaError extends Error {\n constructor(message: string) {\n super(`[incorta-sdk] ${message}`);\n this.name = \"IncortaError\";\n }\n}\n\n/**\n * The client was configured with missing or invalid values.\n *\n * Thrown before any network call, so it always indicates a caller mistake\n * rather than a server or connectivity problem.\n */\nexport class IncortaConfigError extends IncortaError {\n constructor(message: string) {\n super(message);\n this.name = \"IncortaConfigError\";\n }\n}\n\n/**\n * The request carried no signed-in user.\n *\n * Every call in this SDK acts *as* an Incorta user, so there is no anonymous\n * mode to fall back to. Send the visitor through `auth.handler`'s login route\n * (or let `auth.gate` do it) and retry once a session exists.\n */\nexport class IncortaAuthRequiredError extends IncortaError {\n constructor(\n message = \"No Incorta session on this request. Sign the user in first — mount `client.auth.handler` and redirect to its /login route, or wrap the app in `client.auth.gate`.\",\n ) {\n super(message);\n this.name = \"IncortaAuthRequiredError\";\n }\n}\n\n/**\n * The session's access token expired while the client was still holding it.\n *\n * A scoped client captures the token as it stood when the session was read, so\n * a long-lived one eventually goes stale. Re-read the session\n * (`client.forRequest(request)` per request) to pick up a refreshed token —\n * `@incorta/auth` renews it automatically while reading.\n */\nexport class IncortaSessionExpiredError extends IncortaError {\n constructor(\n /** Epoch millis at which the captured token expired. */\n readonly expiredAt: number,\n ) {\n super(\n `The Incorta access token on this client expired at ${new Date(expiredAt).toISOString()}. ` +\n \"Re-read the session (client.forRequest(request)) to get a refreshed token.\",\n );\n this.name = \"IncortaSessionExpiredError\";\n }\n}\n\n/** The Incorta environment could not be reached (DNS, TLS, refused connection). */\nexport class IncortaConnectionError extends IncortaError {\n constructor(\n message: string,\n /** The underlying `fetch` failure, when there was one. */\n readonly cause?: unknown,\n ) {\n super(message);\n this.name = \"IncortaConnectionError\";\n }\n}\n\n/** The request exceeded the configured timeout. */\nexport class IncortaTimeoutError extends IncortaConnectionError {\n constructor(message: string) {\n super(message);\n this.name = \"IncortaTimeoutError\";\n }\n}\n\n/** The Incorta API returned a non-success HTTP status. */\nexport class IncortaApiError extends IncortaError {\n /** HTTP status code of the response. */\n readonly statusCode: number;\n /** Incorta error code such as `\"INC_09030108\"`, when the response carried one. */\n readonly code: string | undefined;\n /** Human-readable message with the `INC_` prefix stripped. */\n readonly detail: string;\n /** Raw response text, truncated for readability. */\n readonly responseBody: string;\n /** The request URL. Never carries credentials — the token travels in a header. */\n readonly url: string | undefined;\n\n constructor(\n statusCode: number,\n detail: string,\n options: { code?: string; responseBody?: string; url?: string } = {},\n ) {\n const prefix = options.code ? `[${options.code}] ` : \"\";\n super(`HTTP ${statusCode}: ${prefix}${detail}`);\n this.name = \"IncortaApiError\";\n this.statusCode = statusCode;\n this.code = options.code;\n this.detail = detail;\n this.responseBody = (options.responseBody ?? \"\").slice(0, BODY_LIMIT);\n this.url = options.url;\n }\n}\n\n/**\n * Incorta rejected the user's access token (HTTP 401).\n *\n * Distinct from {@link IncortaSessionExpiredError}, which is raised locally\n * before the request goes out. This one means the token reached Incorta and\n * came back refused — a revoked session, a client whose registration changed,\n * or a token minted for a different tenant.\n */\nexport class AuthenticationError extends IncortaApiError {\n constructor(...args: ConstructorParameters<typeof IncortaApiError>) {\n super(...args);\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * The user is authenticated but lacks access to the requested resource (403).\n *\n * Metadata is read with the signed-in user's own token, so this reflects that\n * user's Incorta permissions — not a defect in the app's configuration.\n */\nexport class PermissionDeniedError extends IncortaApiError {\n constructor(...args: ConstructorParameters<typeof IncortaApiError>) {\n super(...args);\n this.name = \"PermissionDeniedError\";\n }\n}\n\n/** The requested resource does not exist (HTTP 404). */\nexport class NotFoundError extends IncortaApiError {\n constructor(...args: ConstructorParameters<typeof IncortaApiError>) {\n super(...args);\n this.name = \"NotFoundError\";\n }\n}\n\n/** The named schema does not exist, or this user cannot see it. */\nexport class SchemaNotFoundError extends NotFoundError {\n constructor(...args: ConstructorParameters<typeof IncortaApiError>) {\n super(...args);\n this.name = \"SchemaNotFoundError\";\n }\n}\n\n/** Incorta reported an internal error (HTTP 5xx). */\nexport class IncortaServerError extends IncortaApiError {\n constructor(...args: ConstructorParameters<typeof IncortaApiError>) {\n super(...args);\n this.name = \"IncortaServerError\";\n }\n}\n\n/**\n * No table or view with the given name exists in the schema.\n *\n * Thrown client-side: the API returns the whole schema in one call, so a\n * missing table is detected locally rather than by a failed request.\n */\nexport class TableNotFoundError extends IncortaError {\n constructor(\n readonly schemaName: string,\n readonly tableName: string,\n /** Names that *do* exist in the schema, to make the typo obvious. */\n readonly available: string[] = [],\n ) {\n const preview = [...available].sort().slice(0, 10).join(\", \");\n const suffix = available.length > 10 ? \", ...\" : \"\";\n super(\n `Table or view \"${tableName}\" not found in schema \"${schemaName}\".` +\n (preview ? ` Available: ${preview}${suffix}` : \"\"),\n );\n this.name = \"TableNotFoundError\";\n }\n}\n\nconst STATUS_MAP: Record<\n number,\n new (...args: ConstructorParameters<typeof IncortaApiError>) => IncortaApiError\n> = {\n 401: AuthenticationError,\n 403: PermissionDeniedError,\n 404: NotFoundError,\n};\n\n/**\n * Builds the most specific error subclass for an error response.\n *\n * @param statusCode HTTP status code.\n * @param body Raw response text.\n * @param options.url Request URL, for the error message.\n * @param options.payload Already-parsed JSON body, when available.\n */\nexport function apiErrorFromResponse(\n statusCode: number,\n body: string,\n options: { url?: string; payload?: unknown } = {},\n): IncortaApiError {\n let detail = \"\";\n const payload = options.payload;\n if (payload && typeof payload === \"object\") {\n const record = payload as Record<string, unknown>;\n const candidate = record.message ?? record.error;\n if (typeof candidate === \"string\") {\n detail = candidate;\n } else if (Array.isArray(record.errorMessages)) {\n // The query endpoint reports validation failures as\n // {\"errorMessages\": [{\"message\": \"INC_...: ...\"}]} instead.\n detail = record.errorMessages\n .map((item) =>\n item && typeof item === \"object\"\n ? (item as Record<string, unknown>).message\n : undefined,\n )\n .filter((message): message is string => typeof message === \"string\")\n .join(\"; \");\n }\n }\n if (!detail) {\n detail = body.trim() || `Request failed with status ${statusCode}`;\n }\n\n let code: string | undefined;\n const match = CODE_PATTERN.exec(detail);\n if (match?.[1]) {\n code = match[1];\n detail = (match[2] ?? \"\").trim();\n }\n\n const ErrorClass =\n STATUS_MAP[statusCode] ?? (statusCode >= 500 ? IncortaServerError : IncortaApiError);\n\n return new ErrorClass(statusCode, detail, { code, responseBody: body, url: options.url });\n}\n","/**\n * Values the Incorta API accepts and returns.\n *\n * Each is a string-literal union (compile-time safety) paired with a frozen\n * array of its members (runtime validation, for JavaScript callers who get no\n * type checking).\n */\n\n/**\n * Filter for `client.schemas.list()`.\n *\n * Passing an unrecognised value to the API is *silently* treated as `BUSINESS`\n * and returns HTTP 200, so a typo yields plausible-looking but wrong results.\n * The client validates locally before sending the request to make that failure\n * loud.\n */\nexport const SCHEMA_TYPES = [\"ALL\", \"PHYSICAL\", \"BUSINESS\"] as const;\nexport type SchemaType = (typeof SCHEMA_TYPES)[number];\n\n/** Ordering accepted by the schema list endpoint. */\nexport const SORT_ORDERS = [\"NAME_ASC\", \"NAME_DESC\"] as const;\nexport type SortBy = (typeof SORT_ORDERS)[number];\n\n/**\n * Normalised type of a schema object.\n *\n * The API reports business views as `\"BUSSINESS_VIEW\"` (three S's). That\n * spelling is preserved as the value for round-tripping, but {@link parseObjectType}\n * accepts the corrected spelling too, so callers keep working if Incorta ever\n * fixes it.\n */\nexport type ObjectType = \"table\" | \"BUSSINESS_VIEW\" | \"UNKNOWN\";\n\n/**\n * Converts an API type string into an {@link ObjectType}.\n *\n * Unrecognised values map to `\"UNKNOWN\"` rather than throwing: an unfamiliar\n * object type should not break metadata listing.\n */\nexport function parseObjectType(raw: string | null | undefined): ObjectType {\n if (!raw) return \"UNKNOWN\";\n const normalised = raw.trim().toUpperCase();\n if (normalised === \"TABLE\") return \"table\";\n if (normalised === \"BUSSINESS_VIEW\" || normalised === \"BUSINESS_VIEW\") {\n return \"BUSSINESS_VIEW\";\n }\n return \"UNKNOWN\";\n}\n\n/**\n * Result format for a data query.\n *\n * `csv` comes back as one text blob rather than parsed rows, and cannot be\n * combined with an unstringified response: asking for both returns only the\n * header line, with HTTP 200. The client blocks that combination.\n */\nexport const QUERY_FORMATS = [\"json\", \"csv\"] as const;\nexport type QueryFormat = (typeof QUERY_FORMATS)[number];\n\n/** Comparison operator for a filter. */\nexport const FILTER_OPS = [\"=\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"BETWEEN\", \"IN_LIST\"] as const;\nexport type FilterOp = (typeof FILTER_OPS)[number];\n\n/** Aggregation function applied to a measure. */\nexport const AGGREGATIONS = [\n \"sum\",\n \"count\",\n \"distinct\",\n \"median\",\n \"average\",\n \"min\",\n \"max\",\n] as const;\nexport type Aggregation = (typeof AGGREGATIONS)[number];\n\n/**\n * How null cells are rendered in a result.\n *\n * Incorta's published API schema also lists `DASH`, but the server rejects it\n * with HTTP 400 — it is deliberately absent here rather than offered and broken.\n */\nexport const NULL_VALUE_AS = [\"NULL\", \"EMPTY\"] as const;\nexport type NullValueAs = (typeof NULL_VALUE_AS)[number];\n\n/** Direction of a sort criterion. */\nexport const SORT_DIRECTIONS = [\"asc\", \"desc\"] as const;\nexport type SortDirection = (typeof SORT_DIRECTIONS)[number];\n\n/** Semantic role Incorta assigns to a column. */\nexport const COLUMN_FUNCTIONS = [\"key\", \"dimension\", \"measure\", \"attribute\", \"unknown\"] as const;\nexport type ColumnFunction = (typeof COLUMN_FUNCTIONS)[number];\n\n/** Converts an API function string into a {@link ColumnFunction}. */\nexport function parseColumnFunction(raw: string | null | undefined): ColumnFunction {\n if (!raw) return \"unknown\";\n const normalised = raw.trim().toLowerCase();\n return (COLUMN_FUNCTIONS as readonly string[]).includes(normalised)\n ? (normalised as ColumnFunction)\n : \"unknown\";\n}\n","/**\n * Internal HTTP transport.\n *\n * Owns the auth header, the retry policy, and the translation of error\n * responses into the error hierarchy. Not part of the public API.\n *\n * The bearer token is resolved per request rather than captured once: it\n * belongs to a user session that `@incorta/auth` refreshes underneath us, so\n * reading it late is what keeps a long-lived scoped client honest.\n */\nimport type { ResolvedConfig } from \"./config.js\";\nimport {\n IncortaApiError,\n IncortaConnectionError,\n IncortaTimeoutError,\n apiErrorFromResponse,\n} from \"./errors.js\";\n\n/**\n * Supplies the access token for one request. Throws\n * {@link IncortaSessionExpiredError} when the session it draws on has aged out.\n */\nexport type TokenProvider = () => string | Promise<string>;\n\n/** Everything a resource needs to make a call as one user. */\nexport interface RequestContext {\n config: ResolvedConfig;\n getToken: TokenProvider;\n}\n\n/**\n * Status codes worth retrying: transient server and rate-limit failures only.\n * 4xx client errors are never retried, since replaying them cannot help.\n */\nconst RETRY_STATUSES = new Set([429, 500, 502, 503, 504]);\n\n/** First backoff step; doubles each attempt (0.5s, 1s, 2s, ...). */\nconst BACKOFF_BASE_MS = 500;\n\nconst USER_AGENT = \"incorta-sdk-ts\";\n\n/**\n * Builds an API URL, percent-encoding each path segment.\n *\n * Encoding matters: schema names may contain spaces or slashes that would\n * otherwise change which endpoint is addressed.\n */\nexport function urlFor(config: ResolvedConfig, ...segments: string[]): string {\n const encoded = segments.map((segment) => encodeURIComponent(segment)).join(\"/\");\n return `${config.apiRoot}/${encoded}`;\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** `Retry-After` in seconds (or an HTTP date), when the server sent one. */\nfunction retryAfterMs(response: Response): number | null {\n const header = response.headers.get(\"retry-after\");\n if (!header) return null;\n const seconds = Number(header);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const date = Date.parse(header);\n return Number.isNaN(date) ? null : Math.max(0, date - Date.now());\n}\n\nfunction isTimeout(error: unknown): boolean {\n return error instanceof Error && (error.name === \"TimeoutError\" || error.name === \"AbortError\");\n}\n\nexport interface PostOptions {\n /** JSON request body. */\n body?: Record<string, unknown>;\n /** Query-string parameters. */\n params?: Record<string, string>;\n}\n\n/**\n * POSTs to the Incorta API as the context's user and returns the decoded JSON\n * body.\n *\n * @throws {IncortaSessionExpiredError} The user's token expired before the call.\n * @throws {IncortaTimeoutError} The request exceeded `timeoutMs`.\n * @throws {IncortaConnectionError} The environment was unreachable.\n * @throws {IncortaApiError} A non-2xx response, as the matching subclass.\n */\nexport async function postJson(\n context: RequestContext,\n url: string,\n options: PostOptions = {},\n): Promise<unknown> {\n const { config } = context;\n const target = options.params\n ? `${url}?${new URLSearchParams(options.params).toString()}`\n : url;\n\n // attempt 0 is the initial call; maxRetries additional attempts follow.\n for (let attempt = 0; ; attempt += 1) {\n // Re-resolved each attempt: a retry after a long backoff should not send a\n // token that went stale while we waited.\n const token = await context.getToken();\n\n let response: Response;\n try {\n response = await config.fetch(target, {\n method: \"POST\",\n headers: {\n authorization: `Bearer ${token}`,\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"user-agent\": USER_AGENT,\n },\n body: JSON.stringify(options.body ?? {}),\n signal: AbortSignal.timeout(config.timeoutMs),\n });\n } catch (error) {\n if (isTimeout(error)) {\n // The timeout budget is per request and was just spent; retrying the\n // same call would only multiply the wait.\n throw new IncortaTimeoutError(\n `Request to ${target} timed out after ${config.timeoutMs}ms.`,\n );\n }\n if (attempt < config.maxRetries) {\n await sleep(BACKOFF_BASE_MS * 2 ** attempt);\n continue;\n }\n throw new IncortaConnectionError(\n `Could not reach ${target}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n\n if (RETRY_STATUSES.has(response.status) && attempt < config.maxRetries) {\n await sleep(retryAfterMs(response) ?? BACKOFF_BASE_MS * 2 ** attempt);\n continue;\n }\n\n return decode(response, target);\n }\n}\n\nasync function decode(response: Response, url: string): Promise<unknown> {\n const body = await response.text();\n\n let payload: unknown;\n if (body.trim()) {\n try {\n payload = JSON.parse(body);\n } catch {\n payload = undefined;\n }\n }\n\n if (!response.ok) {\n throw apiErrorFromResponse(response.status, body, { url, payload });\n }\n\n if (payload === undefined) {\n throw new IncortaApiError(\n response.status,\n \"Expected a JSON response from Incorta but the body was not valid JSON.\",\n { responseBody: body, url },\n );\n }\n\n return payload;\n}\n","/**\n * Querying data out of business views.\n *\n * Where the schema resources describe *what exists*, this one reads *what is in\n * it*. It wraps the single `POST {apiRoot}/query` endpoint, which takes an\n * `InsightQuery` and returns a pivot: measures, optionally grouped by row and\n * column dimensions.\n *\n * Fields are addressed by their fully qualified name, `SCHEMA.VIEW.COLUMN` —\n * for example `HR_BS.Employee_BS.SALARY`.\n *\n * Three behaviours of that endpoint are worth knowing, because all three fail\n * quietly rather than with an error:\n *\n * - Omitting `aggregate` makes Incorta aggregate anyway. A non-aggregate\n * extract written without it returns **zero rows with HTTP 200**, and reports\n * string columns as `double`. This client always sends the flag.\n * - An aggregate query ignores the top-level `sorting` list entirely; sorting\n * is read only from within a dimension. This client routes each sort onto the\n * dimension it names, and rejects one that matches none.\n * - `format: \"csv\"` combined with an unstringified response returns only the\n * header line, also with HTTP 200. `csv()` handles the encoding itself.\n */\nimport {\n AGGREGATIONS,\n type Aggregation,\n FILTER_OPS,\n type FilterOp,\n NULL_VALUE_AS,\n type NullValueAs,\n QUERY_FORMATS,\n type QueryFormat,\n SORT_DIRECTIONS,\n type SortDirection,\n} from \"./enums.js\";\nimport { IncortaConfigError } from \"./errors.js\";\nimport { type RequestContext, postJson, urlFor } from \"./http.js\";\n\n/** Rows per request when `iterRows` is not given a size. */\nexport const DEFAULT_QUERY_PAGE_SIZE = 1000;\n\n/** One sorting criterion. */\nexport interface Sort {\n /** Fully qualified column to sort on. */\n field?: string;\n /** Expression to sort on, instead of `field`. */\n formula?: string;\n /** @default \"asc\" */\n dir?: SortDirection;\n label?: string;\n /** Sort by the nth measure rather than a dimension. */\n measureIndex?: number;\n}\n\n/** A filter applied to the source rows, before any aggregation. */\nexport interface Filter {\n type: \"fieldKey\" | \"formulaKey\";\n /** Required when `type` is `\"fieldKey\"`. */\n fieldKey?: string;\n /** Required when `type` is `\"formulaKey\"`. */\n formulaKey?: string;\n op?: FilterOp;\n values?: (string | number)[];\n label?: string;\n prompt?: boolean;\n caseSensitive?: boolean;\n}\n\n/** A filter applied *after* aggregation — Incorta's `HAVING`. */\nexport interface AggregateFilter {\n field?: string;\n formula?: string;\n op?: FilterOp;\n values?: (string | number)[];\n aggregation?: Aggregation;\n}\n\n/**\n * A value to return.\n *\n * In an aggregate query, `aggregation` folds the column; in a non-aggregate\n * query it is left unset and the raw values come back.\n */\nexport interface Measure {\n field?: string;\n formula?: string;\n label?: string;\n aggregation?: Aggregation;\n /** Value scale: `\"k\"`, `\"m\"`, or `\"percent\"`. */\n scale?: string;\n /** In hierarchy min/max queries, the physical column referenced. */\n sourceField?: string;\n filters?: Filter[];\n}\n\n/** A column to group by — used for both `rows` and `columns`. */\nexport interface Dimension {\n field?: string;\n formula?: string;\n label?: string;\n sorting?: Sort[];\n /** Marks the field as part of a time series (year, quarter, month, day). */\n datePart?: boolean;\n /** Emit a subtotal row for this level. Row dimensions only. */\n subTotal?: boolean;\n /** Keep groups with no matching rows. Row dimensions only. */\n showEmptyGroups?: boolean;\n}\n\n/** A measure given as a bare field name is shorthand for `{ field }`. */\nexport type MeasureInput = Measure | string;\n/** A dimension given as a bare field name is shorthand for `{ field }`. */\nexport type DimensionInput = Dimension | string;\n\nexport interface QueryOptions {\n /** Dimensions to group down the page. */\n rows?: DimensionInput[];\n /** Dimensions to pivot across the page. */\n columns?: DimensionInput[];\n /**\n * `false` for a flat extract, `true` to fold the measures with their\n * aggregation functions. Always sent explicitly — see the module docstring.\n * @default false\n */\n aggregate?: boolean;\n /** Applied to source rows, before aggregation. */\n filters?: Filter[];\n /** Applied after aggregation — Incorta's `HAVING`. */\n aggregateFilters?: AggregateFilter[];\n /** Result ordering. */\n sorting?: Sort[];\n /** Maximum rows to return; `0` means the server default. @default 0 */\n pageSize?: number;\n /** Zero-based row to start from. @default 0 */\n startRow?: number;\n /** Return values with Incorta's display formatting applied. */\n formatted?: boolean;\n /** How null cells are rendered. */\n nullValueAs?: NullValueAs;\n /** Return sample data when the result exceeds the sample size. */\n sampled?: boolean;\n /**\n * Incorta login name to impersonate. Base64-encoded for you; requires the\n * caller to hold impersonation rights.\n */\n asUser?: string;\n /**\n * Merged into the `InsightQuery` untouched, for fields this interface does\n * not name (`ensureParentSubtotal`, `skipSelf`, `sortWithinGroups`,\n * `distinctFilter`, `auditingInfo`).\n */\n advanced?: Record<string, unknown>;\n}\n\n/** One column of a result set, in the order it appears in each row. */\nexport interface ResultColumn {\n label: string;\n dataType: string;\n /** Which part of the pivot this column came from. */\n kind: \"row\" | \"column\" | \"measure\";\n raw: Record<string, unknown>;\n}\n\n/**\n * The rows a query returned, plus the paging state that produced them.\n *\n * Rows come back as arrays of strings in column order — Incorta renders every\n * cell as text, including numbers. `records()` zips them against `headers` when\n * a mapping is easier to work with.\n */\nexport interface QueryResult {\n /** Column descriptors, ordered rows, then columns, then measures. */\n columns: ResultColumn[];\n /** Column labels, in cell order. */\n headers: string[];\n rows: string[][];\n /** Rows matching the query in full, beyond this page. */\n totalRows: number;\n startRow: number;\n endRow: number;\n /** Whether this response covered every matching row. */\n complete: boolean;\n /** Whether rows remain beyond this page. */\n hasMore: boolean;\n isAggregated: boolean;\n /** Whether the data exceeded the sample size and was sampled. */\n isSampled: boolean;\n /** The undecoded response, for fields this model does not name. */\n raw: Record<string, unknown>;\n /**\n * The rows as `{ header: cell }` objects. Duplicate labels are disambiguated\n * with a positional suffix, so no column is silently dropped.\n */\n records(): Record<string, string>[];\n /** Every cell of one column, by label, matched case-insensitively. */\n column(label: string): string[];\n}\n\nexport interface DataResource {\n /**\n * Runs one query and returns its rows.\n *\n * @throws {IncortaConfigError} The query is malformed — caught locally,\n * before the request is sent.\n * @throws {PermissionDeniedError} The user may not read this data, or may not\n * impersonate `asUser`.\n */\n query(measures: MeasureInput[], options?: QueryOptions): Promise<QueryResult>;\n /**\n * Yields every matching row, fetching one page at a time.\n *\n * Preferable to a single unbounded query on a large view, where one response\n * can be slow and memory-hungry. `startRow` is driven internally.\n */\n iterRows(\n measures: MeasureInput[],\n options?: Omit<QueryOptions, \"startRow\">,\n ): AsyncGenerator<string[], void, undefined>;\n /**\n * Runs a query and returns the result as CSV text.\n *\n * Incorta renders the CSV itself, so this returns the body verbatim rather\n * than re-encoding `query`'s rows. Note that the endpoint applies `pageSize`\n * to JSON results only — a CSV request returns every matching row regardless.\n */\n csv(measures: MeasureInput[], options?: QueryOptions): Promise<string>;\n /**\n * Sends a request body verbatim and returns the decoded response.\n *\n * The escape hatch for query features this resource does not model. The body\n * is sent unvalidated, so the endpoint's quiet defaults apply — set\n * `query.aggregate` explicitly.\n */\n raw(body: Record<string, unknown>): Promise<unknown>;\n /**\n * Builds the request body without sending it. Exposed for inspection,\n * logging, and tests.\n */\n buildBody(\n measures: MeasureInput[],\n options?: QueryOptions & { format?: QueryFormat },\n ): Record<string, unknown>;\n}\n\nfunction oneOf<T extends string>(\n value: string,\n allowed: readonly T[],\n what: string,\n normalise: (raw: string) => string = (raw) => raw,\n): T {\n const candidate = normalise(String(value).trim());\n if ((allowed as readonly string[]).includes(candidate)) return candidate as T;\n throw new IncortaConfigError(\n `Invalid ${what} \"${value}\". Expected one of: ${allowed.join(\", \")}.`,\n );\n}\n\nfunction validateNullValueAs(value: NullValueAs): NullValueAs {\n if (String(value).trim().toUpperCase() === \"DASH\") {\n throw new IncortaConfigError(\n \"nullValueAs \\\"DASH\\\" appears in Incorta's API documentation but the server \" +\n \"rejects it with HTTP 400. Use NULL or EMPTY.\",\n );\n }\n return oneOf(value, NULL_VALUE_AS, \"null representation\", (raw) => raw.toUpperCase());\n}\n\n/** Drops keys whose value is `undefined` or an empty array. */\nfunction clean(payload: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(payload)) {\n if (value === undefined) continue;\n if (Array.isArray(value) && value.length === 0) continue;\n out[key] = value;\n }\n return out;\n}\n\nfunction requireFieldOrFormula(\n field: string | undefined,\n formula: string | undefined,\n what: string,\n): void {\n if (!field && !formula) {\n throw new IncortaConfigError(`${what} needs either a field or a formula.`);\n }\n if (field && formula) {\n throw new IncortaConfigError(\n `${what} takes a field or a formula, not both — Incorta would ignore one silently.`,\n );\n }\n}\n\nfunction toMeasure(value: MeasureInput): Measure {\n return typeof value === \"string\" ? { field: value } : value;\n}\n\nfunction toDimension(value: DimensionInput): Dimension {\n return typeof value === \"string\" ? { field: value } : value;\n}\n\nfunction sortToApi(sort: Sort): Record<string, unknown> {\n return clean({\n field: sort.field,\n formula: sort.formula,\n label: sort.label,\n dir: oneOf(sort.dir ?? \"asc\", SORT_DIRECTIONS, \"sort direction\", (raw) => raw.toLowerCase()),\n measureIndex: sort.measureIndex,\n });\n}\n\nfunction filterToApi(filter: Filter): Record<string, unknown> {\n if (filter.type !== \"fieldKey\" && filter.type !== \"formulaKey\") {\n throw new IncortaConfigError(\n `Invalid filter type \"${filter.type}\". Expected \"fieldKey\" or \"formulaKey\".`,\n );\n }\n if (filter.type === \"fieldKey\" && !filter.fieldKey) {\n throw new IncortaConfigError(\"A fieldKey filter needs fieldKey.\");\n }\n if (filter.type === \"formulaKey\" && !filter.formulaKey) {\n throw new IncortaConfigError(\"A formulaKey filter needs formulaKey.\");\n }\n\n const op = filter.op === undefined ? undefined : oneOf(filter.op, FILTER_OPS, \"filter operator\");\n const values = (filter.values ?? []).map(String);\n if (op === \"BETWEEN\" && values.length !== 2) {\n throw new IncortaConfigError(\"BETWEEN needs exactly two values.\");\n }\n\n const value = clean({\n values,\n op,\n options: filter.caseSensitive === undefined ? undefined : { caseSensitive: filter.caseSensitive },\n });\n\n return clean({\n type: filter.type,\n label: filter.label,\n fieldKey: filter.fieldKey,\n formulaKey: filter.formulaKey,\n value: Object.keys(value).length > 0 ? value : undefined,\n prompt: filter.prompt || undefined,\n });\n}\n\nfunction aggregateFilterToApi(filter: AggregateFilter): Record<string, unknown> {\n requireFieldOrFormula(filter.field, filter.formula, \"An aggregate filter\");\n\n const op = filter.op === undefined ? undefined : oneOf(filter.op, FILTER_OPS, \"filter operator\");\n if (op === \"IN_LIST\") {\n throw new IncortaConfigError(\"IN_LIST is not accepted by aggregate filters.\");\n }\n const values = (filter.values ?? []).map(String);\n if (op === \"BETWEEN\" && values.length !== 2) {\n throw new IncortaConfigError(\"BETWEEN needs exactly two values.\");\n }\n\n return clean({\n field: filter.field,\n formula: filter.formula,\n op,\n values,\n aggregation:\n filter.aggregation === undefined\n ? undefined\n : oneOf(filter.aggregation, AGGREGATIONS, \"aggregation\", (raw) => raw.toLowerCase()),\n });\n}\n\nfunction measureToApi(measure: Measure): Record<string, unknown> {\n requireFieldOrFormula(measure.field, measure.formula, \"A measure\");\n return clean({\n field: measure.field,\n formula: measure.formula,\n label: measure.label,\n aggregation:\n measure.aggregation === undefined\n ? undefined\n : oneOf(measure.aggregation, AGGREGATIONS, \"aggregation\", (raw) => raw.toLowerCase()),\n scale: measure.scale,\n sourceField: measure.sourceField,\n filters: (measure.filters ?? []).map(filterToApi),\n });\n}\n\nfunction dimensionToApi(dimension: Dimension, asRow: boolean): Record<string, unknown> {\n requireFieldOrFormula(dimension.field, dimension.formula, \"A dimension\");\n if (!asRow && (dimension.subTotal || dimension.showEmptyGroups)) {\n // The column dimension schema has neither key, and the endpoint rejects\n // unknown properties outright.\n throw new IncortaConfigError(\"subTotal and showEmptyGroups apply to row dimensions only.\");\n }\n return clean({\n field: dimension.field,\n formula: dimension.formula,\n label: dimension.label,\n sorting: (dimension.sorting ?? []).map(sortToApi),\n datePart: dimension.datePart || undefined,\n subTotal: asRow ? dimension.subTotal || undefined : undefined,\n showEmptyGroups: asRow ? dimension.showEmptyGroups || undefined : undefined,\n });\n}\n\n/**\n * Moves each sort onto the dimension it names, in place.\n *\n * Aggregate queries read sorting only from within a dimension. A sort matching\n * no dimension would be accepted and ignored, so it throws instead.\n */\nfunction attachSorts(\n sorting: Sort[],\n dims: Dimension[],\n payloads: Record<string, unknown>[],\n): void {\n for (const sort of sorting) {\n const target = sort.field ?? sort.formula;\n const index = dims.findIndex(\n (dim) => target !== undefined && (dim.field === target || dim.formula === target),\n );\n if (index === -1) {\n const available = dims.map((dim) => dim.field ?? dim.formula ?? \"?\");\n throw new IncortaConfigError(\n `Cannot sort an aggregate query by \"${target}\": it is not one of its dimensions ` +\n `(${available.join(\", \") || \"none given\"}). Incorta ignores sorting on anything ` +\n \"else in an aggregate query and returns HTTP 200, so this is rejected rather \" +\n \"than silently dropped.\",\n );\n }\n const payload = payloads[index] as Record<string, unknown>;\n const existing = (payload.sorting as Record<string, unknown>[] | undefined) ?? [];\n payload.sorting = [...existing, sortToApi(sort)];\n }\n}\n\n/** Suffixes repeated labels so an object keeps every column. */\nfunction uniqueLabels(labels: string[]): string[] {\n const seen = new Map<string, number>();\n return labels.map((label) => {\n const count = seen.get(label) ?? 0;\n seen.set(label, count + 1);\n return count === 0 ? label : `${label}_${count + 1}`;\n });\n}\n\nfunction headersOf(raw: unknown, kind: ResultColumn[\"kind\"]): ResultColumn[] {\n if (!Array.isArray(raw)) return [];\n return raw\n .filter((item): item is Record<string, unknown> => typeof item === \"object\" && item !== null)\n .map((item) => ({\n label: String(item.label ?? \"\"),\n dataType: String(item.dataType ?? \"\"),\n kind,\n raw: item,\n }));\n}\n\nfunction intOf(payload: Record<string, unknown>, key: string): number {\n const value = Number(payload[key]);\n return Number.isFinite(value) ? Math.trunc(value) : 0;\n}\n\n/**\n * Builds a result from the endpoint's response body.\n *\n * Cells arrive as one flat array per row, ordered row dimensions, then column\n * dimensions, then measures; the headers are reported in three separate lists\n * and are stitched back into that same order here.\n */\nexport function parseQueryResult(payload: Record<string, unknown>): QueryResult {\n const columns = [\n ...headersOf(payload.rowHeaders, \"row\"),\n ...headersOf(payload.colHeaders, \"column\"),\n ...headersOf(payload.measureHeaders, \"measure\"),\n ];\n const rows = (Array.isArray(payload.data) ? payload.data : [])\n .filter((row): row is unknown[] => Array.isArray(row))\n .map((row) => row.map((cell) => (cell === null || cell === undefined ? \"\" : String(cell))));\n\n const headers = columns.map((column) => column.label);\n const totalRows = intOf(payload, \"totalRows\");\n const endRow = intOf(payload, \"endRow\");\n const complete = payload.complete === undefined ? true : Boolean(payload.complete);\n\n return {\n columns,\n headers,\n rows,\n totalRows,\n startRow: intOf(payload, \"startRow\"),\n endRow,\n complete,\n hasMore: !complete && endRow < totalRows,\n isAggregated: Boolean(payload.isAggregated),\n isSampled: Boolean(payload.isSampled),\n raw: payload,\n records() {\n const keys = uniqueLabels(headers);\n return rows.map((row) => {\n const record: Record<string, string> = {};\n keys.forEach((key, index) => {\n record[key] = row[index] ?? \"\";\n });\n return record;\n });\n },\n column(label: string) {\n const target = label.toLowerCase();\n const index = headers.findIndex((header) => header.toLowerCase() === target);\n if (index === -1) {\n throw new IncortaConfigError(\n `No column \"${label}\" in this result. Available: ${headers.join(\", \")}.`,\n );\n }\n return rows.map((row) => row[index] ?? \"\");\n },\n };\n}\n\n/**\n * Reads rows from business views, as the signed-in user.\n *\n * Row-level security is applied by Incorta against the token on the request, so\n * two users running the same query legitimately see different rows.\n */\nexport function createDataResource(context: RequestContext): DataResource {\n function buildBody(\n measures: MeasureInput[],\n options: QueryOptions & { format?: QueryFormat } = {},\n ): Record<string, unknown> {\n if (!measures || measures.length === 0) {\n throw new IncortaConfigError(\n \"A query needs at least one measure — the field or fields to return.\",\n );\n }\n\n const pageSize = options.pageSize ?? 0;\n const startRow = options.startRow ?? 0;\n if (!Number.isInteger(pageSize) || pageSize < 0) {\n throw new IncortaConfigError(\"pageSize must be an integer >= 0 (0 means the server default).\");\n }\n if (!Number.isInteger(startRow) || startRow < 0) {\n throw new IncortaConfigError(\"startRow must be an integer >= 0.\");\n }\n\n const format = oneOf(options.format ?? \"json\", QUERY_FORMATS, \"result format\", (raw) =>\n raw.toLowerCase(),\n );\n const aggregate = options.aggregate ?? false;\n\n const rowDims = (options.rows ?? []).map(toDimension);\n const colDims = (options.columns ?? []).map(toDimension);\n const rowPayloads = rowDims.map((dim) => dimensionToApi(dim, true));\n const colPayloads = colDims.map((dim) => dimensionToApi(dim, false));\n\n const query: Record<string, unknown> = {\n // Always explicit. Omitting it means \"aggregate\", and a non-aggregate\n // extract then silently returns zero rows.\n aggregate,\n format,\n measures: measures.map((measure) => measureToApi(toMeasure(measure))),\n };\n\n const sorting = options.sorting ?? [];\n if (aggregate) {\n // An aggregate query ignores the top-level sorting list outright. Route\n // each sort onto the dimension it names, so it takes effect rather than\n // disappearing with a 200.\n attachSorts(sorting, [...rowDims, ...colDims], [...rowPayloads, ...colPayloads]);\n } else if (sorting.length > 0) {\n query.sorting = sorting.map(sortToApi);\n }\n\n if (rowPayloads.length > 0) query.rows = rowPayloads;\n if (colPayloads.length > 0) query.columns = colPayloads;\n if (options.filters?.length) query.filters = options.filters.map(filterToApi);\n if (options.aggregateFilters?.length) {\n query.aggregateFilters = options.aggregateFilters.map(aggregateFilterToApi);\n }\n if (pageSize) query.pageSize = pageSize;\n if (startRow) query.startRow = startRow;\n if (options.formatted) query.formatted = true;\n if (options.sampled) query.sampled = true;\n if (options.nullValueAs !== undefined) {\n query.nullValueAs = validateNullValueAs(options.nullValueAs);\n }\n Object.assign(query, options.advanced ?? {});\n\n const body: Record<string, unknown> = { query };\n\n // JSON results are requested unstringified so the response arrives as an\n // object rather than JSON-encoded text. CSV must stay stringified: asking\n // for CSV unstringified returns the header line alone, with HTTP 200.\n if (format === \"json\") body.stringify = false;\n\n if (options.asUser !== undefined) {\n if (!options.asUser.trim()) {\n throw new IncortaConfigError(\"asUser must be a non-empty login name.\");\n }\n body.username = Buffer.from(options.asUser, \"utf8\").toString(\"base64\");\n }\n\n return body;\n }\n\n function send(body: Record<string, unknown>): Promise<unknown> {\n return postJson(context, urlFor(context.config, \"query\"), { body });\n }\n\n async function query(\n measures: MeasureInput[],\n options: QueryOptions = {},\n ): Promise<QueryResult> {\n const payload = await send(buildBody(measures, { ...options, format: \"json\" }));\n return parseQueryResult((payload ?? {}) as Record<string, unknown>);\n }\n\n return {\n query,\n buildBody,\n raw: send,\n async *iterRows(measures, options = {}) {\n const pageSize = options.pageSize ?? DEFAULT_QUERY_PAGE_SIZE;\n if (!Number.isInteger(pageSize) || pageSize <= 0) {\n throw new IncortaConfigError(\"pageSize must be a positive integer.\");\n }\n\n let startRow = 0;\n for (;;) {\n const page = await query(measures, { ...options, pageSize, startRow });\n yield* page.rows;\n\n // Guard against a server that ignores paging: without this, an endpoint\n // that always returns the same page would loop forever.\n if (page.rows.length === 0 || !page.hasMore) return;\n startRow += page.rows.length;\n }\n },\n async csv(measures, options = {}) {\n const payload = await send(buildBody(measures, { ...options, format: \"csv\" }));\n const text =\n payload && typeof payload === \"object\"\n ? (payload as Record<string, unknown>).data\n : undefined;\n if (typeof text !== \"string\") {\n throw new IncortaConfigError(\n \"Expected a CSV payload under 'data' but Incorta returned something else.\",\n );\n }\n return text;\n },\n };\n}\n","/**\n * Typed models for Incorta metadata.\n *\n * The API returns structurally different payloads for physical and business\n * schemas, and the two are modelled separately rather than merged:\n *\n * - A **physical** schema responds with `tablesDetails` only; the\n * `viewsDetails` key is absent entirely.\n * - A **business** schema responds with `viewsDetails` only.\n *\n * Their columns differ too. Both carry `name`/`label`/`description`/`dataType`/\n * `function`; table columns add `formula` and `isEncrypt`, while view columns add\n * `isFormula` and `source` (the fully qualified lineage of the underlying\n * physical column).\n *\n * The models are plain data — no classes — so results survive\n * `structuredClone`, `JSON.stringify`, and a trip through a worker or an HTTP\n * response. Where Python uses `isinstance`, these use a `kind` discriminant,\n * which narrows correctly in TypeScript and survives serialisation.\n *\n * Every parser tolerates missing keys, so a server-side addition or omission\n * degrades to a default instead of throwing.\n */\nimport {\n type ColumnFunction,\n type ObjectType,\n parseColumnFunction,\n parseObjectType,\n} from \"./enums.js\";\n\ntype Payload = Record<string, unknown>;\n\nfunction str(payload: Payload, key: string, fallback = \"\"): string {\n const value = payload[key];\n if (value === null || value === undefined) return fallback;\n return typeof value === \"string\" ? value : String(value);\n}\n\nfunction bool(payload: Payload, key: string): boolean {\n return Boolean(payload[key]);\n}\n\nfunction int(payload: Payload, key: string): number {\n const value = Number(payload[key]);\n return Number.isFinite(value) ? Math.trunc(value) : 0;\n}\n\n/**\n * Converts Incorta's epoch-milliseconds timestamps to `Date`.\n *\n * Returns `null` for the `0` sentinel the API uses to mean \"never\".\n */\nfunction epochMillisToDate(value: unknown): Date | null {\n const millis = Number(value);\n if (!Number.isFinite(millis) || millis <= 0) return null;\n return new Date(millis);\n}\n\nfunction records(value: unknown): Payload[] {\n if (!Array.isArray(value)) return [];\n return value.filter((item): item is Payload => typeof item === \"object\" && item !== null);\n}\n\n/** Fields common to table and view columns. */\nexport interface ColumnBase {\n name: string;\n label: string;\n description: string;\n dataType: string;\n function: ColumnFunction;\n /** The untouched API record, for fields this client does not model. */\n raw: Payload;\n}\n\n/** A column of a physical table. */\nexport interface TableColumn extends ColumnBase {\n kind: \"tableColumn\";\n formula: string;\n isEncrypted: boolean;\n /** Whether Incorta treats this column as a key. */\n isKey: boolean;\n}\n\n/** A column of a business view. */\nexport interface ViewColumn extends ColumnBase {\n kind: \"viewColumn\";\n /**\n * Fully qualified lineage of the backing physical column, such as\n * `\"OnlineStore.customer.AccountNumber\"`. Empty for formula columns.\n */\n source: string;\n /** Whether the column is computed rather than sourced. */\n isFormula: boolean;\n}\n\nexport type Column = TableColumn | ViewColumn;\n\nfunction parseTableColumn(payload: Payload): TableColumn {\n const fn = parseColumnFunction(str(payload, \"function\") || null);\n return {\n kind: \"tableColumn\",\n name: str(payload, \"name\"),\n label: str(payload, \"label\"),\n description: str(payload, \"description\"),\n dataType: str(payload, \"dataType\"),\n function: fn,\n formula: str(payload, \"formula\"),\n isEncrypted: bool(payload, \"isEncrypt\"),\n isKey: fn === \"key\",\n raw: payload,\n };\n}\n\nfunction parseViewColumn(payload: Payload): ViewColumn {\n return {\n kind: \"viewColumn\",\n name: str(payload, \"name\"),\n label: str(payload, \"label\"),\n description: str(payload, \"description\"),\n dataType: str(payload, \"dataType\"),\n function: parseColumnFunction(str(payload, \"function\") || null),\n source: str(payload, \"source\"),\n isFormula: bool(payload, \"isFormula\"),\n raw: payload,\n };\n}\n\n/** Fields common to tables and views. */\ninterface SchemaObjectBase {\n name: string;\n type: ObjectType;\n owner: string;\n description: string;\n schemaName: string;\n /** `schema.object` — the form used to address an object in a query. */\n qualifiedName: string;\n columnNames: readonly string[];\n raw: Payload;\n}\n\n/** A physical table. */\nexport interface Table extends SchemaObjectBase {\n kind: \"table\";\n columns: readonly TableColumn[];\n /** Columns Incorta marks as keys. */\n keys: readonly TableColumn[];\n /**\n * Row count as last reported by Incorta; `0` when the table has not been\n * loaded or the count is unavailable.\n */\n rowsCount: number;\n multiDataSource: boolean;\n hierarchy: boolean;\n /** Timestamp of the most recent load, or `null`. */\n lastVersion: Date | null;\n}\n\n/** A business view. */\nexport interface View extends SchemaObjectBase {\n kind: \"view\";\n columns: readonly ViewColumn[];\n /** The view's base table when Incorta reports one; often empty. */\n baseTable: string;\n /** Distinct physical columns this view draws from, in order. */\n sources: readonly string[];\n}\n\nexport type SchemaObject = Table | View;\n\nfunction qualify(schemaName: string, name: string): string {\n return schemaName ? `${schemaName}.${name}` : name;\n}\n\nfunction parseTable(payload: Payload, schemaName: string): Table {\n const columns = records(payload.columns).map(parseTableColumn);\n const name = str(payload, \"name\");\n return {\n kind: \"table\",\n name,\n type: parseObjectType(str(payload, \"type\") || null),\n owner: str(payload, \"owner\"),\n description: str(payload, \"description\"),\n schemaName,\n qualifiedName: qualify(schemaName, name),\n columns,\n columnNames: columns.map((column) => column.name),\n keys: columns.filter((column) => column.isKey),\n rowsCount: int(payload, \"rowsCount\"),\n multiDataSource: bool(payload, \"multiDataSource\"),\n hierarchy: bool(payload, \"hierarchy\"),\n lastVersion: epochMillisToDate(payload.lastVersionTimestamp),\n raw: payload,\n };\n}\n\nfunction parseView(payload: Payload, schemaName: string): View {\n const columns = records(payload.columns).map(parseViewColumn);\n const name = str(payload, \"name\");\n return {\n kind: \"view\",\n name,\n type: parseObjectType(str(payload, \"type\") || null),\n owner: str(payload, \"owner\"),\n description: str(payload, \"description\"),\n schemaName,\n qualifiedName: qualify(schemaName, name),\n columns,\n columnNames: columns.map((column) => column.name),\n baseTable: str(payload, \"baseTable\"),\n sources: [...new Set(columns.map((column) => column.source).filter(Boolean))],\n raw: payload,\n };\n}\n\n/**\n * Summary of a schema as returned by the schema list endpoint.\n *\n * This is the lightweight form: it names the schema but carries no tables or\n * views. Call `client.schemas.get()` to fetch the contents.\n */\nexport interface SchemaInfo {\n id: number;\n name: string;\n description: string;\n type: string;\n owner: string;\n lastModified: Date | null;\n isEmpty: boolean;\n isPhysical: boolean;\n isBusiness: boolean;\n raw: Payload;\n}\n\nexport function parseSchemaInfo(payload: Payload): SchemaInfo {\n const type = str(payload, \"schemaType\");\n return {\n id: int(payload, \"schemaID\"),\n name: str(payload, \"schemaName\"),\n description: str(payload, \"schemaDescription\"),\n type,\n owner: str(payload, \"owner\"),\n lastModified: epochMillisToDate(payload.lastModified),\n isEmpty: bool(payload, \"isEmpty\"),\n isPhysical: type.toUpperCase() === \"PHYSICAL\",\n isBusiness: type.toUpperCase() === \"BUSINESS\",\n raw: payload,\n };\n}\n\ninterface SchemaBase {\n name: string;\n total: number;\n /** Every table or view, whichever this schema holds. */\n objects: readonly SchemaObject[];\n objectNames: readonly string[];\n raw: Payload;\n}\n\n/** A physical schema, containing tables. */\nexport interface PhysicalSchema extends SchemaBase {\n kind: \"physical\";\n objects: readonly Table[];\n tables: readonly Table[];\n}\n\n/** A business schema, containing views. */\nexport interface BusinessSchema extends SchemaBase {\n kind: \"business\";\n objects: readonly View[];\n views: readonly View[];\n}\n\nexport type Schema = PhysicalSchema | BusinessSchema;\n\n/**\n * Builds the right schema shape by inspecting which key is present.\n *\n * A physical schema carries `tablesDetails` and a business schema carries\n * `viewsDetails`; the other key is absent. When neither is present (an empty\n * schema), a physical schema with no tables is returned, since that is the more\n * common case for an empty schema.\n */\nexport function parseSchema(payload: Payload, name: string): Schema {\n const total = int(payload, \"total\");\n\n const viewsPayload = records(payload.viewsDetails);\n if (viewsPayload.length > 0) {\n const views = viewsPayload.map((item) => parseView(item, name));\n return {\n kind: \"business\",\n name,\n total,\n objects: views,\n objectNames: views.map((view) => view.name),\n views,\n raw: payload,\n };\n }\n\n const tables = records(payload.tablesDetails).map((item) => parseTable(item, name));\n return {\n kind: \"physical\",\n name,\n total,\n objects: tables,\n objectNames: tables.map((table) => table.name),\n tables,\n raw: payload,\n };\n}\n\n/** Looks up a table or view by name, case-insensitively. */\nexport function findObject(schema: Schema, name: string): SchemaObject | undefined {\n const target = name.toLowerCase();\n return schema.objects.find((object) => object.name.toLowerCase() === target);\n}\n\n/** Looks up one column of a table or view by name, case-insensitively. */\nexport function findColumn(object: SchemaObject, name: string): Column | undefined {\n const target = name.toLowerCase();\n return object.columns.find((column) => column.name.toLowerCase() === target);\n}\n\n/** One page of results, plus the paging window that produced it. */\nexport interface Page<T> {\n /** The records in this page. */\n items: readonly T[];\n /** Total records available server-side, across all pages. */\n total: number;\n /** Page size requested; `0` means \"no limit\" to the API. */\n limit: number;\n /** Zero-based index this page started at. */\n offset: number;\n /** Whether more records exist beyond this page. */\n hasMore: boolean;\n}\n","/**\n * Schema listing and retrieval.\n *\n * Every call is made with the signed-in user's own access token, so results are\n * already filtered to what that user may see in Incorta — there is no\n * app-level identity to over-share from.\n */\nimport { SCHEMA_TYPES, SORT_ORDERS, type SchemaType, type SortBy } from \"./enums.js\";\nimport { IncortaConfigError, NotFoundError, SchemaNotFoundError } from \"./errors.js\";\nimport { type RequestContext, postJson, urlFor } from \"./http.js\";\nimport { type Page, type Schema, type SchemaInfo, parseSchema, parseSchemaInfo } from \"./models.js\";\n\n/** Page size used by `iterAll` when the caller does not choose one. */\nexport const DEFAULT_PAGE_SIZE = 100;\n\nexport interface ListSchemasOptions {\n /**\n * `ALL`, `PHYSICAL`, or `BUSINESS`. Validated locally because the API\n * silently falls back to `BUSINESS` for any unrecognised value instead of\n * returning an error.\n * @default \"ALL\"\n */\n type?: SchemaType;\n /** Maximum schemas to return; `0` means all. @default 0 */\n limit?: number;\n /** Zero-based index to start from. @default 0 */\n offset?: number;\n /** Result ordering. @default \"NAME_ASC\" */\n sortBy?: SortBy;\n}\n\nexport interface IterSchemasOptions {\n type?: SchemaType;\n /** Records per request; must be positive. @default 100 */\n pageSize?: number;\n sortBy?: SortBy;\n}\n\nexport interface SchemasResource {\n /**\n * Lists the schemas this user can see, without their tables or views.\n *\n * @throws {IncortaConfigError} `type` or `sortBy` is not a valid value.\n */\n list(options?: ListSchemasOptions): Promise<SchemaInfo[]>;\n /**\n * Like {@link list}, but also returns the server-side total. Use it when you\n * need to know how many schemas exist beyond the current page.\n */\n listPage(options?: ListSchemasOptions): Promise<Page<SchemaInfo>>;\n /**\n * Yields every schema, fetching one page at a time.\n *\n * Preferable to `limit: 0` on large tenants, where a single unbounded\n * response can be slow and memory-hungry.\n */\n iterAll(options?: IterSchemasOptions): AsyncGenerator<SchemaInfo, void, undefined>;\n /** Lists physical schemas. Shorthand for `list({ type: \"PHYSICAL\" })`. */\n physical(options?: Omit<ListSchemasOptions, \"type\">): Promise<SchemaInfo[]>;\n /** Lists business schemas. Shorthand for `list({ type: \"BUSINESS\" })`. */\n business(options?: Omit<ListSchemasOptions, \"type\">): Promise<SchemaInfo[]>;\n /**\n * Fetches one schema with all of its tables or views.\n *\n * Returns a `PhysicalSchema` or a `BusinessSchema` depending on what the\n * environment reports; switch on `.kind` to narrow.\n *\n * @throws {SchemaNotFoundError} No such schema, or this user cannot see it.\n * @throws {IncortaConfigError} `name` is empty.\n */\n get(name: string): Promise<Schema>;\n /** Whether a schema with this name is visible to this user. */\n exists(name: string): Promise<boolean>;\n}\n\n/** Rejects invalid schema types before the API silently defaults them. */\nfunction validateSchemaType(value: SchemaType | undefined): SchemaType {\n if (value === undefined) return \"ALL\";\n const normalised = String(value).trim().toUpperCase();\n if ((SCHEMA_TYPES as readonly string[]).includes(normalised)) return normalised as SchemaType;\n throw new IncortaConfigError(\n `Invalid schema type \"${value}\". Expected one of: ${SCHEMA_TYPES.join(\", \")}. ` +\n \"Note the Incorta API accepts unknown values silently and returns BUSINESS \" +\n \"schemas, so this is validated client-side.\",\n );\n}\n\nfunction validateSortBy(value: SortBy | undefined): SortBy {\n if (value === undefined) return \"NAME_ASC\";\n const normalised = String(value).trim().toUpperCase();\n if ((SORT_ORDERS as readonly string[]).includes(normalised)) return normalised as SortBy;\n throw new IncortaConfigError(\n `Invalid sort order \"${value}\". Expected one of: ${SORT_ORDERS.join(\", \")}.`,\n );\n}\n\nfunction validateWindow(limit: number, offset: number): void {\n if (!Number.isInteger(limit) || limit < 0) {\n throw new IncortaConfigError(\"limit must be an integer >= 0 (0 means no limit).\");\n }\n if (!Number.isInteger(offset) || offset < 0) {\n throw new IncortaConfigError(\"offset must be an integer >= 0.\");\n }\n}\n\n/** Extracts a list-of-records field, tolerating absence or a wrong type. */\nfunction details(payload: unknown, key: string): Record<string, unknown>[] {\n if (!payload || typeof payload !== \"object\") return [];\n const value = (payload as Record<string, unknown>)[key];\n if (!Array.isArray(value)) return [];\n return value.filter(\n (item): item is Record<string, unknown> => typeof item === \"object\" && item !== null,\n );\n}\n\nfunction total(payload: unknown, fallback: number): number {\n if (!payload || typeof payload !== \"object\") return fallback;\n const value = Number((payload as Record<string, unknown>).total);\n return Number.isFinite(value) ? Math.trunc(value) : fallback;\n}\n\nexport function createSchemasResource(context: RequestContext): SchemasResource {\n async function listPage(options: ListSchemasOptions = {}): Promise<Page<SchemaInfo>> {\n const schemaType = validateSchemaType(options.type);\n const ordering = validateSortBy(options.sortBy);\n const limit = options.limit ?? 0;\n const offset = options.offset ?? 0;\n validateWindow(limit, offset);\n\n const payload = await postJson(context, urlFor(context.config, \"schema\", \"list\"), {\n params: { schemaType },\n body: { limit, offset, sortBy: ordering },\n });\n\n const items = details(payload, \"schemasDetails\").map(parseSchemaInfo);\n const serverTotal = total(payload, items.length);\n\n return {\n items,\n total: serverTotal,\n limit,\n offset,\n hasMore: limit === 0 ? false : offset + items.length < serverTotal,\n };\n }\n\n async function list(options: ListSchemasOptions = {}): Promise<SchemaInfo[]> {\n const page = await listPage(options);\n return [...page.items];\n }\n\n async function get(name: string): Promise<Schema> {\n if (!name || !name.trim()) {\n throw new IncortaConfigError(\"Schema name must be a non-empty string.\");\n }\n\n let payload: unknown;\n try {\n payload = await postJson(context, urlFor(context.config, \"schema\", name, \"list\"), {\n body: { limit: 0, offset: 0 },\n });\n } catch (error) {\n if (error instanceof NotFoundError) {\n throw new SchemaNotFoundError(error.statusCode, error.detail, {\n code: error.code,\n responseBody: error.responseBody,\n url: error.url,\n });\n }\n throw error;\n }\n\n return parseSchema(payload as Record<string, unknown>, name);\n }\n\n return {\n list,\n listPage,\n async *iterAll(options: IterSchemasOptions = {}) {\n const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;\n if (!Number.isInteger(pageSize) || pageSize <= 0) {\n throw new IncortaConfigError(\"pageSize must be a positive integer.\");\n }\n\n let offset = 0;\n for (;;) {\n const page = await listPage({\n type: options.type,\n sortBy: options.sortBy,\n limit: pageSize,\n offset,\n });\n yield* page.items;\n\n // Guard against a server that ignores paging: without this, an endpoint\n // that always returns the same page would loop forever.\n if (page.items.length === 0 || !page.hasMore) return;\n offset += page.items.length;\n }\n },\n physical: (options = {}) => list({ ...options, type: \"PHYSICAL\" }),\n business: (options = {}) => list({ ...options, type: \"BUSINESS\" }),\n get,\n async exists(name) {\n try {\n await get(name);\n return true;\n } catch (error) {\n if (error instanceof SchemaNotFoundError) return false;\n throw error;\n }\n },\n };\n}\n","/**\n * Table and view retrieval.\n *\n * Incorta has no per-table endpoint: the schema endpoint returns every table\n * with its columns in one response. These methods therefore fetch the schema and\n * resolve locally, so each call costs one HTTP request. Use `list` once and\n * filter in TypeScript when inspecting several objects in the same schema.\n */\nimport { IncortaConfigError, TableNotFoundError } from \"./errors.js\";\nimport type { Column, SchemaObject, Table, View } from \"./models.js\";\nimport { findObject } from \"./models.js\";\nimport type { SchemasResource } from \"./schemas.js\";\n\nexport interface TablesResource {\n /**\n * Fetches one table or view by name.\n *\n * Searches physical tables and business views alike, so callers do not need to\n * know which kind of schema they are addressing. Switch on `.kind`\n * (`\"table\"` / `\"view\"`) to narrow the result.\n *\n * @throws {SchemaNotFoundError} The schema does not exist.\n * @throws {TableNotFoundError} The schema exists but holds no such object.\n * @throws {IncortaConfigError} `tableName` is empty.\n */\n get(schemaName: string, tableName: string): Promise<SchemaObject>;\n /** Lists every table and view in a schema. */\n list(schemaName: string): Promise<SchemaObject[]>;\n /** Lists the names of every table and view in a schema. */\n names(schemaName: string): Promise<string[]>;\n /** Fetches the columns of one table or view. */\n columns(schemaName: string, tableName: string): Promise<Column[]>;\n /** Whether the named table or view exists in the schema. */\n exists(schemaName: string, tableName: string): Promise<boolean>;\n /** Lists only physical tables, skipping views. */\n tablesOnly(schemaName: string): Promise<Table[]>;\n /** Lists only business views, skipping tables. */\n viewsOnly(schemaName: string): Promise<View[]>;\n}\n\nexport function createTablesResource(schemas: SchemasResource): TablesResource {\n async function get(\n schemaName: string,\n tableName: string,\n ): Promise<SchemaObject> {\n if (!tableName || !tableName.trim()) {\n throw new IncortaConfigError(\"Table name must be a non-empty string.\");\n }\n\n const schema = await schemas.get(schemaName);\n const found = findObject(schema, tableName);\n if (!found) {\n throw new TableNotFoundError(schemaName, tableName, [...schema.objectNames]);\n }\n return found;\n }\n\n return {\n get,\n async list(schemaName) {\n const schema = await schemas.get(schemaName);\n return [...schema.objects];\n },\n async names(schemaName) {\n const schema = await schemas.get(schemaName);\n return [...schema.objectNames];\n },\n async columns(schemaName, tableName) {\n const object = await get(schemaName, tableName);\n return [...object.columns];\n },\n async exists(schemaName, tableName) {\n try {\n await get(schemaName, tableName);\n return true;\n } catch (error) {\n if (error instanceof TableNotFoundError) return false;\n throw error;\n }\n },\n async tablesOnly(schemaName) {\n const schema = await schemas.get(schemaName);\n return schema.kind === \"physical\" ? [...schema.tables] : [];\n },\n async viewsOnly(schemaName) {\n const schema = await schemas.get(schemaName);\n return schema.kind === \"business\" ? [...schema.views] : [];\n },\n };\n}\n","/**\n * The per-user client.\n *\n * Metadata in this SDK is always read *as somebody*: the token comes from an\n * Incorta OAuth session, so two users of the same app see two different\n * catalogs. Nothing is cached — every call reads Incorta live, so a schema\n * changed in Incorta is visible on the next call and no user's catalog can\n * outlive the request that fetched it.\n */\nimport type { IncortaUser } from \"@incorta/auth\";\n\nimport type { ResolvedConfig } from \"./config.js\";\nimport type { RequestContext, TokenProvider } from \"./http.js\";\nimport { type DataResource, createDataResource } from \"./query.js\";\nimport { type SchemasResource, createSchemasResource } from \"./schemas.js\";\nimport { type TablesResource, createTablesResource } from \"./tables.js\";\n\n/** Non-secret view of a scoped client, safe to log. */\nexport interface UserClientInfo {\n baseUrl: string;\n tenant: string;\n /** Incorta login name of the user this client acts as, when known. */\n user: string | undefined;\n /** Epoch millis when this client's access token expires; `0` when unknown. */\n accessTokenExpiresAt: number;\n}\n\nexport interface IncortaUserClient {\n /**\n * The Incorta user this client acts as. `null` for a client built from a\n * bare access token, where no identity was supplied alongside it.\n */\n user: IncortaUser | null;\n /** Schema listing and retrieval, as this user. */\n schemas: SchemasResource;\n /** Table, view, and column retrieval, as this user. */\n tables: TablesResource;\n /** Row queries against business views, as this user. */\n data: DataResource;\n /**\n * The resolved configuration, minus the token.\n *\n * The token is deliberately absent so that logging the client — or\n * serialising it into an error report — cannot leak a user's credential.\n */\n info: UserClientInfo;\n}\n\nexport interface UserClientOptions {\n /** Resolves the bearer token for each request. */\n getToken: TokenProvider;\n /** The identity behind the token, when it is known. */\n user?: IncortaUser | null;\n /** Epoch millis the token expires at, for {@link UserClientInfo}. */\n accessTokenExpiresAt?: number;\n}\n\nexport function createUserClient(\n config: ResolvedConfig,\n options: UserClientOptions,\n): IncortaUserClient {\n const context: RequestContext = { config, getToken: options.getToken };\n\n const schemas = createSchemasResource(context);\n const user = options.user ?? null;\n\n return {\n user,\n schemas,\n tables: createTablesResource(schemas),\n data: createDataResource(context),\n info: {\n baseUrl: config.baseUrl,\n tenant: config.tenant,\n user: user?.sub,\n accessTokenExpiresAt: options.accessTokenExpiresAt ?? 0,\n },\n };\n}\n","import type { AuthSession, IncortaAuth, IncortaUser } from \"@incorta/auth\";\n\nimport { type IncortaClientConfig, type ResolvedConfig, resolveConfig } from \"./core/config.js\";\nimport { IncortaAuthRequiredError, IncortaSessionExpiredError } from \"./core/errors.js\";\nimport { type IncortaUserClient, createUserClient } from \"./core/user-client.js\";\n\nexport type { IncortaClientConfig } from \"./core/config.js\";\nexport {\n AGGREGATIONS,\n COLUMN_FUNCTIONS,\n FILTER_OPS,\n NULL_VALUE_AS,\n QUERY_FORMATS,\n SCHEMA_TYPES,\n SORT_DIRECTIONS,\n SORT_ORDERS,\n parseColumnFunction,\n parseObjectType,\n} from \"./core/enums.js\";\nexport type {\n Aggregation,\n ColumnFunction,\n FilterOp,\n NullValueAs,\n ObjectType,\n QueryFormat,\n SchemaType,\n SortBy,\n SortDirection,\n} from \"./core/enums.js\";\nexport {\n AuthenticationError,\n IncortaApiError,\n IncortaAuthRequiredError,\n IncortaConfigError,\n IncortaConnectionError,\n IncortaError,\n IncortaServerError,\n IncortaSessionExpiredError,\n IncortaTimeoutError,\n NotFoundError,\n PermissionDeniedError,\n SchemaNotFoundError,\n TableNotFoundError,\n} from \"./core/errors.js\";\nexport { findColumn, findObject } from \"./core/models.js\";\nexport type {\n BusinessSchema,\n Column,\n ColumnBase,\n Page,\n PhysicalSchema,\n Schema,\n SchemaInfo,\n SchemaObject,\n Table,\n TableColumn,\n View,\n ViewColumn,\n} from \"./core/models.js\";\nexport { DEFAULT_QUERY_PAGE_SIZE, parseQueryResult } from \"./core/query.js\";\nexport type {\n AggregateFilter,\n DataResource,\n Dimension,\n DimensionInput,\n Filter,\n Measure,\n MeasureInput,\n QueryOptions,\n QueryResult,\n ResultColumn,\n Sort,\n} from \"./core/query.js\";\nexport { DEFAULT_PAGE_SIZE } from \"./core/schemas.js\";\nexport type {\n IterSchemasOptions,\n ListSchemasOptions,\n SchemasResource,\n} from \"./core/schemas.js\";\nexport type { TablesResource } from \"./core/tables.js\";\nexport type {\n IncortaUserClient,\n UserClientInfo,\n UserClientOptions,\n} from \"./core/user-client.js\";\n\n// Re-exported so callers can type their own session-handling code without\n// adding a second import of @incorta/auth.\nexport type { AuthSession, IncortaAuth, IncortaUser } from \"@incorta/auth\";\n\n/** Non-secret view of the resolved configuration, safe to log. */\nexport interface ClientInfo {\n baseUrl: string;\n tenant: string;\n timeoutMs: number;\n maxRetries: number;\n}\n\nexport interface IncortaClient {\n /**\n * The OAuth instance this client reads sessions from.\n *\n * Mount `auth.handler` to serve the login routes and `auth.gate` to require\n * a session, exactly as if you had built it with `createIncortaAuth`\n * yourself — it is the same object when you passed one in.\n */\n auth: IncortaAuth;\n /**\n * Builds a client acting as the user behind `request`, reading (and\n * refreshing) their session.\n *\n * The normal entry point: call it per request, so every batch of metadata\n * calls runs on a freshly refreshed token.\n *\n * @throws {IncortaAuthRequiredError} The request carries no session.\n */\n forRequest(request: Request): Promise<IncortaUserClient>;\n /**\n * Builds a client from a session you already read (via `auth.getSession`, a\n * framework middleware, or a gate).\n *\n * The session's token is captured as-is. `@incorta/auth` refreshes it while\n * *reading* the session, so hold a scoped client no longer than the request\n * that produced it; past the token's expiry, calls raise\n * {@link IncortaSessionExpiredError} rather than 401ing against Incorta.\n */\n forSession(session: AuthSession): IncortaUserClient;\n /**\n * Builds a client from a raw Incorta access token.\n *\n * The escape hatch for tokens that did not arrive as a cookie session — a\n * bearer header from a mobile or SPA client, or a token minted elsewhere in\n * the same tenant. Expiry is not tracked here: an expired token fails as a\n * 401 {@link AuthenticationError} from Incorta.\n */\n forAccessToken(\n accessToken: string,\n options?: { user?: IncortaUser },\n ): IncortaUserClient;\n /** The resolved configuration, minus any secrets. */\n info: ClientInfo;\n}\n\n/**\n * Creates a client for Incorta metadata, authenticated with OAuth 2.0.\n *\n * Unlike a personal-access-token client, this one has no identity of its own:\n * it reads schemas and tables **as the signed-in user**, so results are already\n * scoped to that user's Incorta permissions. Sessions come from\n * `@incorta/auth`.\n *\n * ```ts\n * const client = createIncortaClient(); // reads INCORTA_* from the environment\n *\n * // Serve the OAuth routes with the auth instance the client owns.\n * app.use(\"/auth\", toNodeHandler(client.auth.handler));\n *\n * app.get(\"/api/schemas\", async (req, res) => {\n * const incorta = await client.forRequest(toWebRequest(req));\n * res.json(await incorta.schemas.physical());\n * });\n * ```\n *\n * Pass `auth` when the app already builds its own instance — two instances\n * would mean two sets of OAuth state for the same users:\n *\n * ```ts\n * const auth = createIncortaAuth({ appAccess: \"catalog\" });\n * const client = createIncortaClient({ auth });\n * ```\n *\n * @throws {IncortaConfigError} A required value is missing or malformed.\n */\nexport function createIncortaClient(config: IncortaClientConfig = {}): IncortaClient {\n const resolved: ResolvedConfig = resolveConfig(config);\n\n function forSession(session: AuthSession): IncortaUserClient {\n return createUserClient(resolved, {\n // Checked at call time, not now: a client built at the top of a request\n // should not fail for a token that is still valid when it is used.\n getToken: () => {\n if (session.accessTokenExpiresAt <= Date.now()) {\n throw new IncortaSessionExpiredError(session.accessTokenExpiresAt);\n }\n return session.accessToken;\n },\n user: session.user,\n accessTokenExpiresAt: session.accessTokenExpiresAt,\n });\n }\n\n return {\n auth: resolved.auth,\n forSession,\n async forRequest(request) {\n // getSession refreshes an about-to-expire access token as it reads, so\n // the scoped client starts with the freshest token available.\n const session = await resolved.auth.getSession(request);\n if (!session) throw new IncortaAuthRequiredError();\n return forSession(session);\n },\n forAccessToken(accessToken, options = {}) {\n return createUserClient(resolved, {\n getToken: () => accessToken,\n user: options.user,\n });\n },\n info: {\n baseUrl: resolved.baseUrl,\n tenant: resolved.tenant,\n timeoutMs: resolved.timeoutMs,\n maxRetries: resolved.maxRetries,\n },\n };\n}\n"],"mappings":"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","names":["query","total"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@incorta/sdk",
|
|
3
|
+
"version": "1.8.0",
|
|
4
|
+
"description": "Read Incorta schemas, tables, views, and columns on behalf of the signed-in user — OAuth 2.0 sessions from @incorta/auth, no personal access tokens.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Incorta/IncortaSDK.git",
|
|
9
|
+
"directory": "packages/sdk"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.cjs"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsup",
|
|
29
|
+
"dev": "tsup --watch",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"prepack": "pnpm build"
|
|
33
|
+
},
|
|
34
|
+
"keywords": [
|
|
35
|
+
"incorta",
|
|
36
|
+
"analytics",
|
|
37
|
+
"schema",
|
|
38
|
+
"metadata",
|
|
39
|
+
"oauth",
|
|
40
|
+
"oidc"
|
|
41
|
+
],
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@incorta/auth": "1.8.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.16.5",
|
|
50
|
+
"tsup": "^8.5.0",
|
|
51
|
+
"typescript": "^5.8.3",
|
|
52
|
+
"vitest": "^3.2.4"
|
|
53
|
+
}
|
|
54
|
+
}
|