test-repo/node_modules/iceberg-js/dist/index.cjs

596 lines
17 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use strict';
// src/errors/IcebergError.ts
var IcebergError = class extends Error {
constructor(message, opts) {
super(message);
this.name = "IcebergError";
this.status = opts.status;
this.icebergType = opts.icebergType;
this.icebergCode = opts.icebergCode;
this.details = opts.details;
this.isCommitStateUnknown = opts.icebergType === "CommitStateUnknownException" || [500, 502, 504].includes(opts.status) && opts.icebergType?.includes("CommitState") === true;
}
/**
* Returns true if the error is a 404 Not Found error.
*/
isNotFound() {
return this.status === 404;
}
/**
* Returns true if the error is a 409 Conflict error.
*/
isConflict() {
return this.status === 409;
}
/**
* Returns true if the error is a 419 Authentication Timeout error.
*/
isAuthenticationTimeout() {
return this.status === 419;
}
};
// src/utils/url.ts
function buildUrl(baseUrl, path, query) {
const url = new URL(path, baseUrl);
if (query) {
for (const [key, value] of Object.entries(query)) {
if (value !== void 0) {
url.searchParams.set(key, value);
}
}
}
return url.toString();
}
// src/http/createFetchClient.ts
async function buildAuthHeaders(auth) {
if (!auth || auth.type === "none") {
return {};
}
if (auth.type === "bearer") {
return { Authorization: `Bearer ${auth.token}` };
}
if (auth.type === "header") {
return { [auth.name]: auth.value };
}
if (auth.type === "custom") {
return await auth.getHeaders();
}
return {};
}
function createFetchClient(options) {
const fetchFn = options.fetchImpl ?? globalThis.fetch;
return {
async request({
method,
path,
query,
body,
headers
}) {
const url = buildUrl(options.baseUrl, path, query);
const authHeaders = await buildAuthHeaders(options.auth);
const res = await fetchFn(url, {
method,
headers: {
...body ? { "Content-Type": "application/json" } : {},
...authHeaders,
...headers
},
body: body ? JSON.stringify(body) : void 0
});
const text = await res.text();
const isJson = (res.headers.get("content-type") || "").includes("application/json");
const data = isJson && text ? JSON.parse(text) : text;
if (!res.ok) {
const errBody = isJson ? data : void 0;
const errorDetail = errBody?.error;
throw new IcebergError(
errorDetail?.message ?? `Request failed with status ${res.status}`,
{
status: res.status,
icebergType: errorDetail?.type,
icebergCode: errorDetail?.code,
details: errBody
}
);
}
return { status: res.status, headers: res.headers, data };
}
};
}
// src/catalog/namespaces.ts
function namespaceToPath(namespace) {
return namespace.join("");
}
var NamespaceOperations = class {
constructor(client, prefix = "") {
this.client = client;
this.prefix = prefix;
}
async listNamespaces(parent) {
const query = parent ? { parent: namespaceToPath(parent.namespace) } : void 0;
const response = await this.client.request({
method: "GET",
path: `${this.prefix}/namespaces`,
query
});
return response.data.namespaces.map((ns) => ({ namespace: ns }));
}
async createNamespace(id, metadata) {
const request = {
namespace: id.namespace,
properties: metadata?.properties
};
const response = await this.client.request({
method: "POST",
path: `${this.prefix}/namespaces`,
body: request
});
return response.data;
}
async dropNamespace(id) {
await this.client.request({
method: "DELETE",
path: `${this.prefix}/namespaces/${namespaceToPath(id.namespace)}`
});
}
async loadNamespaceMetadata(id) {
const response = await this.client.request({
method: "GET",
path: `${this.prefix}/namespaces/${namespaceToPath(id.namespace)}`
});
return {
properties: response.data.properties
};
}
async namespaceExists(id) {
try {
await this.client.request({
method: "HEAD",
path: `${this.prefix}/namespaces/${namespaceToPath(id.namespace)}`
});
return true;
} catch (error) {
if (error instanceof IcebergError && error.status === 404) {
return false;
}
throw error;
}
}
async createNamespaceIfNotExists(id, metadata) {
try {
return await this.createNamespace(id, metadata);
} catch (error) {
if (error instanceof IcebergError && error.status === 409) {
return;
}
throw error;
}
}
};
// src/catalog/tables.ts
function namespaceToPath2(namespace) {
return namespace.join("");
}
var TableOperations = class {
constructor(client, prefix = "", accessDelegation) {
this.client = client;
this.prefix = prefix;
this.accessDelegation = accessDelegation;
}
async listTables(namespace) {
const response = await this.client.request({
method: "GET",
path: `${this.prefix}/namespaces/${namespaceToPath2(namespace.namespace)}/tables`
});
return response.data.identifiers;
}
async createTable(namespace, request) {
const headers = {};
if (this.accessDelegation) {
headers["X-Iceberg-Access-Delegation"] = this.accessDelegation;
}
const response = await this.client.request({
method: "POST",
path: `${this.prefix}/namespaces/${namespaceToPath2(namespace.namespace)}/tables`,
body: request,
headers
});
return response.data.metadata;
}
async updateTable(id, request) {
const response = await this.client.request({
method: "POST",
path: `${this.prefix}/namespaces/${namespaceToPath2(id.namespace)}/tables/${id.name}`,
body: request
});
return {
"metadata-location": response.data["metadata-location"],
metadata: response.data.metadata
};
}
async dropTable(id, options) {
await this.client.request({
method: "DELETE",
path: `${this.prefix}/namespaces/${namespaceToPath2(id.namespace)}/tables/${id.name}`,
query: { purgeRequested: String(options?.purge ?? false) }
});
}
async loadTable(id) {
const headers = {};
if (this.accessDelegation) {
headers["X-Iceberg-Access-Delegation"] = this.accessDelegation;
}
const response = await this.client.request({
method: "GET",
path: `${this.prefix}/namespaces/${namespaceToPath2(id.namespace)}/tables/${id.name}`,
headers
});
return response.data.metadata;
}
async tableExists(id) {
const headers = {};
if (this.accessDelegation) {
headers["X-Iceberg-Access-Delegation"] = this.accessDelegation;
}
try {
await this.client.request({
method: "HEAD",
path: `${this.prefix}/namespaces/${namespaceToPath2(id.namespace)}/tables/${id.name}`,
headers
});
return true;
} catch (error) {
if (error instanceof IcebergError && error.status === 404) {
return false;
}
throw error;
}
}
async createTableIfNotExists(namespace, request) {
try {
return await this.createTable(namespace, request);
} catch (error) {
if (error instanceof IcebergError && error.status === 409) {
return await this.loadTable({ namespace: namespace.namespace, name: request.name });
}
throw error;
}
}
};
// src/catalog/IcebergRestCatalog.ts
var IcebergRestCatalog = class {
/**
* Creates a new Iceberg REST Catalog client.
*
* @param options - Configuration options for the catalog client
*/
constructor(options) {
let prefix = "v1";
if (options.catalogName) {
prefix += `/${options.catalogName}`;
}
const baseUrl = options.baseUrl.endsWith("/") ? options.baseUrl : `${options.baseUrl}/`;
this.client = createFetchClient({
baseUrl,
auth: options.auth,
fetchImpl: options.fetch
});
this.accessDelegation = options.accessDelegation?.join(",");
this.namespaceOps = new NamespaceOperations(this.client, prefix);
this.tableOps = new TableOperations(this.client, prefix, this.accessDelegation);
}
/**
* Lists all namespaces in the catalog.
*
* @param parent - Optional parent namespace to list children under
* @returns Array of namespace identifiers
*
* @example
* ```typescript
* // List all top-level namespaces
* const namespaces = await catalog.listNamespaces();
*
* // List namespaces under a parent
* const children = await catalog.listNamespaces({ namespace: ['analytics'] });
* ```
*/
async listNamespaces(parent) {
return this.namespaceOps.listNamespaces(parent);
}
/**
* Creates a new namespace in the catalog.
*
* @param id - Namespace identifier to create
* @param metadata - Optional metadata properties for the namespace
* @returns Response containing the created namespace and its properties
*
* @example
* ```typescript
* const response = await catalog.createNamespace(
* { namespace: ['analytics'] },
* { properties: { owner: 'data-team' } }
* );
* console.log(response.namespace); // ['analytics']
* console.log(response.properties); // { owner: 'data-team', ... }
* ```
*/
async createNamespace(id, metadata) {
return this.namespaceOps.createNamespace(id, metadata);
}
/**
* Drops a namespace from the catalog.
*
* The namespace must be empty (contain no tables) before it can be dropped.
*
* @param id - Namespace identifier to drop
*
* @example
* ```typescript
* await catalog.dropNamespace({ namespace: ['analytics'] });
* ```
*/
async dropNamespace(id) {
await this.namespaceOps.dropNamespace(id);
}
/**
* Loads metadata for a namespace.
*
* @param id - Namespace identifier to load
* @returns Namespace metadata including properties
*
* @example
* ```typescript
* const metadata = await catalog.loadNamespaceMetadata({ namespace: ['analytics'] });
* console.log(metadata.properties);
* ```
*/
async loadNamespaceMetadata(id) {
return this.namespaceOps.loadNamespaceMetadata(id);
}
/**
* Lists all tables in a namespace.
*
* @param namespace - Namespace identifier to list tables from
* @returns Array of table identifiers
*
* @example
* ```typescript
* const tables = await catalog.listTables({ namespace: ['analytics'] });
* console.log(tables); // [{ namespace: ['analytics'], name: 'events' }, ...]
* ```
*/
async listTables(namespace) {
return this.tableOps.listTables(namespace);
}
/**
* Creates a new table in the catalog.
*
* @param namespace - Namespace to create the table in
* @param request - Table creation request including name, schema, partition spec, etc.
* @returns Table metadata for the created table
*
* @example
* ```typescript
* const metadata = await catalog.createTable(
* { namespace: ['analytics'] },
* {
* name: 'events',
* schema: {
* type: 'struct',
* fields: [
* { id: 1, name: 'id', type: 'long', required: true },
* { id: 2, name: 'timestamp', type: 'timestamp', required: true }
* ],
* 'schema-id': 0
* },
* 'partition-spec': {
* 'spec-id': 0,
* fields: [
* { source_id: 2, field_id: 1000, name: 'ts_day', transform: 'day' }
* ]
* }
* }
* );
* ```
*/
async createTable(namespace, request) {
return this.tableOps.createTable(namespace, request);
}
/**
* Updates an existing table's metadata.
*
* Can update the schema, partition spec, or properties of a table.
*
* @param id - Table identifier to update
* @param request - Update request with fields to modify
* @returns Response containing the metadata location and updated table metadata
*
* @example
* ```typescript
* const response = await catalog.updateTable(
* { namespace: ['analytics'], name: 'events' },
* {
* properties: { 'read.split.target-size': '134217728' }
* }
* );
* console.log(response['metadata-location']); // s3://...
* console.log(response.metadata); // TableMetadata object
* ```
*/
async updateTable(id, request) {
return this.tableOps.updateTable(id, request);
}
/**
* Drops a table from the catalog.
*
* @param id - Table identifier to drop
*
* @example
* ```typescript
* await catalog.dropTable({ namespace: ['analytics'], name: 'events' });
* ```
*/
async dropTable(id, options) {
await this.tableOps.dropTable(id, options);
}
/**
* Loads metadata for a table.
*
* @param id - Table identifier to load
* @returns Table metadata including schema, partition spec, location, etc.
*
* @example
* ```typescript
* const metadata = await catalog.loadTable({ namespace: ['analytics'], name: 'events' });
* console.log(metadata.schema);
* console.log(metadata.location);
* ```
*/
async loadTable(id) {
return this.tableOps.loadTable(id);
}
/**
* Checks if a namespace exists in the catalog.
*
* @param id - Namespace identifier to check
* @returns True if the namespace exists, false otherwise
*
* @example
* ```typescript
* const exists = await catalog.namespaceExists({ namespace: ['analytics'] });
* console.log(exists); // true or false
* ```
*/
async namespaceExists(id) {
return this.namespaceOps.namespaceExists(id);
}
/**
* Checks if a table exists in the catalog.
*
* @param id - Table identifier to check
* @returns True if the table exists, false otherwise
*
* @example
* ```typescript
* const exists = await catalog.tableExists({ namespace: ['analytics'], name: 'events' });
* console.log(exists); // true or false
* ```
*/
async tableExists(id) {
return this.tableOps.tableExists(id);
}
/**
* Creates a namespace if it does not exist.
*
* If the namespace already exists, returns void. If created, returns the response.
*
* @param id - Namespace identifier to create
* @param metadata - Optional metadata properties for the namespace
* @returns Response containing the created namespace and its properties, or void if it already exists
*
* @example
* ```typescript
* const response = await catalog.createNamespaceIfNotExists(
* { namespace: ['analytics'] },
* { properties: { owner: 'data-team' } }
* );
* if (response) {
* console.log('Created:', response.namespace);
* } else {
* console.log('Already exists');
* }
* ```
*/
async createNamespaceIfNotExists(id, metadata) {
return this.namespaceOps.createNamespaceIfNotExists(id, metadata);
}
/**
* Creates a table if it does not exist.
*
* If the table already exists, returns its metadata instead.
*
* @param namespace - Namespace to create the table in
* @param request - Table creation request including name, schema, partition spec, etc.
* @returns Table metadata for the created or existing table
*
* @example
* ```typescript
* const metadata = await catalog.createTableIfNotExists(
* { namespace: ['analytics'] },
* {
* name: 'events',
* schema: {
* type: 'struct',
* fields: [
* { id: 1, name: 'id', type: 'long', required: true },
* { id: 2, name: 'timestamp', type: 'timestamp', required: true }
* ],
* 'schema-id': 0
* }
* }
* );
* ```
*/
async createTableIfNotExists(namespace, request) {
return this.tableOps.createTableIfNotExists(namespace, request);
}
};
// src/catalog/types.ts
var DECIMAL_REGEX = /^decimal\s*\(\s*(\d+)\s*,\s*(\d+)\s*\)$/;
var FIXED_REGEX = /^fixed\s*\[\s*(\d+)\s*\]$/;
function parseDecimalType(type) {
const match = type.match(DECIMAL_REGEX);
if (!match) return null;
return {
precision: parseInt(match[1], 10),
scale: parseInt(match[2], 10)
};
}
function parseFixedType(type) {
const match = type.match(FIXED_REGEX);
if (!match) return null;
return {
length: parseInt(match[1], 10)
};
}
function isDecimalType(type) {
return DECIMAL_REGEX.test(type);
}
function isFixedType(type) {
return FIXED_REGEX.test(type);
}
function typesEqual(a, b) {
const decimalA = parseDecimalType(a);
const decimalB = parseDecimalType(b);
if (decimalA && decimalB) {
return decimalA.precision === decimalB.precision && decimalA.scale === decimalB.scale;
}
const fixedA = parseFixedType(a);
const fixedB = parseFixedType(b);
if (fixedA && fixedB) {
return fixedA.length === fixedB.length;
}
return a === b;
}
function getCurrentSchema(metadata) {
return metadata.schemas.find((s) => s["schema-id"] === metadata["current-schema-id"]);
}
exports.IcebergError = IcebergError;
exports.IcebergRestCatalog = IcebergRestCatalog;
exports.getCurrentSchema = getCurrentSchema;
exports.isDecimalType = isDecimalType;
exports.isFixedType = isFixedType;
exports.parseDecimalType = parseDecimalType;
exports.parseFixedType = parseFixedType;
exports.typesEqual = typesEqual;
//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map