iceberg-javascript 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +439 -0
- package/dist/index.cjs +596 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +547 -0
- package/dist/index.d.ts +547 -0
- package/dist/index.mjs +587 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Supabase
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
# iceberg-js
|
|
2
|
+
|
|
3
|
+
[](https://github.com/supabase/iceberg-js/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/iceberg-js)
|
|
5
|
+
[](https://pkg.pr.new/~/supabase/iceberg-js)
|
|
6
|
+
|
|
7
|
+
A small, framework-agnostic JavaScript/TypeScript client for the **Apache Iceberg REST Catalog**.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Generic**: Works with any Iceberg REST Catalog implementation, not tied to any specific vendor
|
|
12
|
+
- **Minimal**: Thin HTTP wrapper over the official REST API, no engine-specific logic
|
|
13
|
+
- **Type-safe**: First-class TypeScript support with strongly-typed request/response models
|
|
14
|
+
- **Fetch-based**: Uses native `fetch` API with support for custom implementations
|
|
15
|
+
- **Universal**: Targets Node 20+ and modern browsers (ES2020)
|
|
16
|
+
- **Catalog-only**: Focused on catalog operations (no data reading/Parquet support in v0.1.0)
|
|
17
|
+
|
|
18
|
+
## Documentation
|
|
19
|
+
|
|
20
|
+
📚 **Full API documentation**: [supabase.github.io/iceberg-js](https://supabase.github.io/iceberg-js/)
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install iceberg-js
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { IcebergRestCatalog } from 'iceberg-js'
|
|
32
|
+
|
|
33
|
+
const catalog = new IcebergRestCatalog({
|
|
34
|
+
baseUrl: 'https://my-catalog.example.com/iceberg/v1',
|
|
35
|
+
auth: {
|
|
36
|
+
type: 'bearer',
|
|
37
|
+
token: process.env.ICEBERG_TOKEN,
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Create a namespace
|
|
42
|
+
await catalog.createNamespace({ namespace: ['analytics'] })
|
|
43
|
+
|
|
44
|
+
// Create a table
|
|
45
|
+
await catalog.createTable(
|
|
46
|
+
{ namespace: ['analytics'] },
|
|
47
|
+
{
|
|
48
|
+
name: 'events',
|
|
49
|
+
schema: {
|
|
50
|
+
type: 'struct',
|
|
51
|
+
fields: [
|
|
52
|
+
{ id: 1, name: 'id', type: 'long', required: true },
|
|
53
|
+
{ id: 2, name: 'timestamp', type: 'timestamp', required: true },
|
|
54
|
+
{ id: 3, name: 'user_id', type: 'string', required: false },
|
|
55
|
+
],
|
|
56
|
+
'schema-id': 0,
|
|
57
|
+
'identifier-field-ids': [1],
|
|
58
|
+
},
|
|
59
|
+
'partition-spec': {
|
|
60
|
+
'spec-id': 0,
|
|
61
|
+
fields: [],
|
|
62
|
+
},
|
|
63
|
+
'write-order': {
|
|
64
|
+
'order-id': 0,
|
|
65
|
+
fields: [],
|
|
66
|
+
},
|
|
67
|
+
properties: {
|
|
68
|
+
'write.format.default': 'parquet',
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### Constructor
|
|
77
|
+
|
|
78
|
+
#### `new IcebergRestCatalog(options)`
|
|
79
|
+
|
|
80
|
+
Creates a new catalog client instance.
|
|
81
|
+
|
|
82
|
+
**Options:**
|
|
83
|
+
|
|
84
|
+
- `baseUrl` (string, required): Base URL of the REST catalog
|
|
85
|
+
- `auth` (AuthConfig, optional): Authentication configuration
|
|
86
|
+
- `catalogName` (string, optional): Catalog name for multi-catalog servers. When specified, requests are sent to `{baseUrl}/v1/{catalogName}/...`. For example, with `baseUrl: 'https://host.com'` and `catalogName: 'prod'`, requests go to `https://host.com/v1/prod/namespaces`
|
|
87
|
+
- `fetch` (typeof fetch, optional): Custom fetch implementation
|
|
88
|
+
- `accessDelegation` (AccessDelegation[], optional): Access delegation mechanisms to request from the server
|
|
89
|
+
|
|
90
|
+
**Authentication types:**
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// No authentication
|
|
94
|
+
{ type: 'none' }
|
|
95
|
+
|
|
96
|
+
// Bearer token
|
|
97
|
+
{ type: 'bearer', token: 'your-token' }
|
|
98
|
+
|
|
99
|
+
// Custom header
|
|
100
|
+
{ type: 'header', name: 'X-Custom-Auth', value: 'secret' }
|
|
101
|
+
|
|
102
|
+
// Custom function
|
|
103
|
+
{ type: 'custom', getHeaders: async () => ({ 'Authorization': 'Bearer ...' }) }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Access Delegation:**
|
|
107
|
+
|
|
108
|
+
Access delegation allows the catalog server to provide temporary credentials or sign requests on your behalf:
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { IcebergRestCatalog } from 'iceberg-js'
|
|
112
|
+
|
|
113
|
+
const catalog = new IcebergRestCatalog({
|
|
114
|
+
baseUrl: 'https://catalog.example.com/iceberg/v1',
|
|
115
|
+
auth: { type: 'bearer', token: 'your-token' },
|
|
116
|
+
// Request vended credentials for data access
|
|
117
|
+
accessDelegation: ['vended-credentials'],
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
// The server may return temporary credentials in the table metadata
|
|
121
|
+
const metadata = await catalog.loadTable({ namespace: ['analytics'], name: 'events' })
|
|
122
|
+
// Use credentials from metadata.config to access table data files
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Supported delegation mechanisms:
|
|
126
|
+
|
|
127
|
+
- `vended-credentials`: Server provides temporary credentials (e.g., AWS STS tokens) for accessing table data
|
|
128
|
+
- `remote-signing`: Server signs data access requests on behalf of the client
|
|
129
|
+
|
|
130
|
+
### Namespace Operations
|
|
131
|
+
|
|
132
|
+
#### `listNamespaces(parent?: NamespaceIdentifier): Promise<NamespaceIdentifier[]>`
|
|
133
|
+
|
|
134
|
+
List all namespaces, optionally under a parent namespace.
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const namespaces = await catalog.listNamespaces()
|
|
138
|
+
// [{ namespace: ['default'] }, { namespace: ['analytics'] }]
|
|
139
|
+
|
|
140
|
+
const children = await catalog.listNamespaces({ namespace: ['analytics'] })
|
|
141
|
+
// [{ namespace: ['analytics', 'prod'] }]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### `createNamespace(id: NamespaceIdentifier, metadata?: NamespaceMetadata): Promise<void>`
|
|
145
|
+
|
|
146
|
+
Create a new namespace with optional properties.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
await catalog.createNamespace({ namespace: ['analytics'] }, { properties: { owner: 'data-team' } })
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### `dropNamespace(id: NamespaceIdentifier): Promise<void>`
|
|
153
|
+
|
|
154
|
+
Drop a namespace. The namespace must be empty.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
await catalog.dropNamespace({ namespace: ['analytics'] })
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### `loadNamespaceMetadata(id: NamespaceIdentifier): Promise<NamespaceMetadata>`
|
|
161
|
+
|
|
162
|
+
Load namespace metadata and properties.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const metadata = await catalog.loadNamespaceMetadata({ namespace: ['analytics'] })
|
|
166
|
+
// { properties: { owner: 'data-team', ... } }
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Table Operations
|
|
170
|
+
|
|
171
|
+
#### `listTables(namespace: NamespaceIdentifier): Promise<TableIdentifier[]>`
|
|
172
|
+
|
|
173
|
+
List all tables in a namespace.
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const tables = await catalog.listTables({ namespace: ['analytics'] })
|
|
177
|
+
// [{ namespace: ['analytics'], name: 'events' }]
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
#### `createTable(namespace: NamespaceIdentifier, request: CreateTableRequest): Promise<TableMetadata>`
|
|
181
|
+
|
|
182
|
+
Create a new table.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
const metadata = await catalog.createTable(
|
|
186
|
+
{ namespace: ['analytics'] },
|
|
187
|
+
{
|
|
188
|
+
name: 'events',
|
|
189
|
+
schema: {
|
|
190
|
+
type: 'struct',
|
|
191
|
+
fields: [
|
|
192
|
+
{ id: 1, name: 'id', type: 'long', required: true },
|
|
193
|
+
{ id: 2, name: 'timestamp', type: 'timestamp', required: true },
|
|
194
|
+
],
|
|
195
|
+
'schema-id': 0,
|
|
196
|
+
},
|
|
197
|
+
'partition-spec': {
|
|
198
|
+
'spec-id': 0,
|
|
199
|
+
fields: [
|
|
200
|
+
{
|
|
201
|
+
source_id: 2,
|
|
202
|
+
field_id: 1000,
|
|
203
|
+
name: 'ts_day',
|
|
204
|
+
transform: 'day',
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### `loadTable(id: TableIdentifier): Promise<TableMetadata>`
|
|
213
|
+
|
|
214
|
+
Load table metadata.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
const metadata = await catalog.loadTable({
|
|
218
|
+
namespace: ['analytics'],
|
|
219
|
+
name: 'events',
|
|
220
|
+
})
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### `updateTable(id: TableIdentifier, request: UpdateTableRequest): Promise<TableMetadata>`
|
|
224
|
+
|
|
225
|
+
Update table metadata (schema, partition spec, or properties).
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
const updated = await catalog.updateTable(
|
|
229
|
+
{ namespace: ['analytics'], name: 'events' },
|
|
230
|
+
{
|
|
231
|
+
properties: { 'read.split.target-size': '134217728' },
|
|
232
|
+
}
|
|
233
|
+
)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### `dropTable(id: TableIdentifier): Promise<void>`
|
|
237
|
+
|
|
238
|
+
Drop a table from the catalog.
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
await catalog.dropTable({ namespace: ['analytics'], name: 'events' })
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Error Handling
|
|
245
|
+
|
|
246
|
+
All API errors throw an `IcebergError` with details from the server:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
import { IcebergError } from 'iceberg-js'
|
|
250
|
+
|
|
251
|
+
try {
|
|
252
|
+
await catalog.loadTable({ namespace: ['test'], name: 'missing' })
|
|
253
|
+
} catch (error) {
|
|
254
|
+
if (error instanceof IcebergError) {
|
|
255
|
+
console.log(error.status) // 404
|
|
256
|
+
console.log(error.icebergType) // 'NoSuchTableException'
|
|
257
|
+
console.log(error.message) // 'Table does not exist'
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## TypeScript Types
|
|
263
|
+
|
|
264
|
+
The library exports all relevant types:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import type {
|
|
268
|
+
NamespaceIdentifier,
|
|
269
|
+
TableIdentifier,
|
|
270
|
+
TableSchema,
|
|
271
|
+
TableField,
|
|
272
|
+
IcebergType,
|
|
273
|
+
PartitionSpec,
|
|
274
|
+
SortOrder,
|
|
275
|
+
CreateTableRequest,
|
|
276
|
+
TableMetadata,
|
|
277
|
+
AuthConfig,
|
|
278
|
+
AccessDelegation,
|
|
279
|
+
} from 'iceberg-js'
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Supported Iceberg Types
|
|
283
|
+
|
|
284
|
+
The following Iceberg primitive types are supported:
|
|
285
|
+
|
|
286
|
+
- `boolean`, `int`, `long`, `float`, `double`
|
|
287
|
+
- `string`, `uuid`, `binary`
|
|
288
|
+
- `date`, `time`, `timestamp`, `timestamptz`
|
|
289
|
+
- `decimal(precision, scale)`, `fixed(length)`
|
|
290
|
+
|
|
291
|
+
## Compatibility
|
|
292
|
+
|
|
293
|
+
This package is built to work in **all** Node.js and JavaScript environments:
|
|
294
|
+
|
|
295
|
+
| Environment | Module System | Import Method | Status |
|
|
296
|
+
| ------------------- | -------------------- | --------------------------------------- | --------------------- |
|
|
297
|
+
| Node.js ESM | `"type": "module"` | `import { ... } from 'iceberg-js'` | ✅ Fully supported |
|
|
298
|
+
| Node.js CommonJS | Default | `const { ... } = require('iceberg-js')` | ✅ Fully supported |
|
|
299
|
+
| TypeScript ESM | `module: "ESNext"` | `import { ... } from 'iceberg-js'` | ✅ Full type support |
|
|
300
|
+
| TypeScript CommonJS | `module: "CommonJS"` | `import { ... } from 'iceberg-js'` | ✅ Full type support |
|
|
301
|
+
| Bundlers | Any | Webpack, Vite, esbuild, Rollup, etc. | ✅ Auto-detected |
|
|
302
|
+
| Browsers | ESM | `<script type="module">` | ✅ Modern browsers |
|
|
303
|
+
| Deno | ESM | `import` from npm: | ✅ With npm specifier |
|
|
304
|
+
|
|
305
|
+
**Package exports:**
|
|
306
|
+
|
|
307
|
+
- ESM: `dist/index.mjs` with `dist/index.d.ts`
|
|
308
|
+
- CommonJS: `dist/index.cjs` with `dist/index.d.cts`
|
|
309
|
+
- Proper `exports` field for Node.js 12+ module resolution
|
|
310
|
+
|
|
311
|
+
All scenarios are tested in CI on Node.js 20 and 22.
|
|
312
|
+
|
|
313
|
+
## Browser Usage
|
|
314
|
+
|
|
315
|
+
The library works in modern browsers that support native `fetch`:
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
import { IcebergRestCatalog } from 'iceberg-js'
|
|
319
|
+
|
|
320
|
+
const catalog = new IcebergRestCatalog({
|
|
321
|
+
baseUrl: 'https://public-catalog.example.com/iceberg/v1',
|
|
322
|
+
auth: { type: 'none' },
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
const namespaces = await catalog.listNamespaces()
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
## Node.js Usage
|
|
329
|
+
|
|
330
|
+
Node.js 20+ includes native `fetch` support. For older versions, provide a custom fetch implementation:
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
import { IcebergRestCatalog } from 'iceberg-js'
|
|
334
|
+
import fetch from 'node-fetch'
|
|
335
|
+
|
|
336
|
+
const catalog = new IcebergRestCatalog({
|
|
337
|
+
baseUrl: 'https://catalog.example.com/iceberg/v1',
|
|
338
|
+
auth: { type: 'bearer', token: 'token' },
|
|
339
|
+
fetch: fetch as any,
|
|
340
|
+
})
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Limitations (v0.1.0)
|
|
344
|
+
|
|
345
|
+
This is a catalog client only. The following are **not supported**:
|
|
346
|
+
|
|
347
|
+
- Reading table data (scanning Parquet files)
|
|
348
|
+
- Writing data to tables
|
|
349
|
+
- Advanced table operations (commits, snapshots, time travel)
|
|
350
|
+
- Views support
|
|
351
|
+
- Multi-table transactions
|
|
352
|
+
|
|
353
|
+
## Development
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
# Install dependencies
|
|
357
|
+
pnpm install
|
|
358
|
+
|
|
359
|
+
# Build the library
|
|
360
|
+
pnpm run build
|
|
361
|
+
|
|
362
|
+
# Run unit tests
|
|
363
|
+
pnpm test
|
|
364
|
+
|
|
365
|
+
# Run integration tests (requires Docker)
|
|
366
|
+
pnpm test:integration
|
|
367
|
+
|
|
368
|
+
# Run integration tests with cleanup (for CI)
|
|
369
|
+
pnpm test:integration:ci
|
|
370
|
+
|
|
371
|
+
# Run compatibility tests (all module systems)
|
|
372
|
+
pnpm test:compatibility
|
|
373
|
+
|
|
374
|
+
# Format code
|
|
375
|
+
pnpm run format
|
|
376
|
+
|
|
377
|
+
# Lint and test
|
|
378
|
+
pnpm run check
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Testing with Docker
|
|
382
|
+
|
|
383
|
+
Integration tests run against a local Iceberg REST Catalog in Docker. See [TESTING-DOCKER.md](./test/integration/TESTING-DOCKER.md) for details.
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# Start Docker services and run integration tests
|
|
387
|
+
pnpm test:integration
|
|
388
|
+
|
|
389
|
+
# Or manually
|
|
390
|
+
docker compose up -d
|
|
391
|
+
npx tsx test/integration/test-local-catalog.ts
|
|
392
|
+
docker compose down -v
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Compatibility Testing
|
|
396
|
+
|
|
397
|
+
The `test:compatibility` script verifies the package works correctly in all JavaScript/TypeScript environments:
|
|
398
|
+
|
|
399
|
+
- **Pure JavaScript ESM** - Projects with `"type": "module"`
|
|
400
|
+
- **Pure JavaScript CommonJS** - Traditional Node.js projects
|
|
401
|
+
- **TypeScript ESM** - TypeScript with `module: "ESNext"`
|
|
402
|
+
- **TypeScript CommonJS** - TypeScript with `module: "CommonJS"`
|
|
403
|
+
|
|
404
|
+
These tests ensure proper module resolution, type definitions, and runtime behavior across all supported environments. See [test/compatibility/README.md](./test/compatibility/README.md) for more details.
|
|
405
|
+
|
|
406
|
+
## License
|
|
407
|
+
|
|
408
|
+
MIT
|
|
409
|
+
|
|
410
|
+
## Releases
|
|
411
|
+
|
|
412
|
+
This project uses [release-please](https://github.com/googleapis/release-please) for automated releases. Here's how it works:
|
|
413
|
+
|
|
414
|
+
1. **Commit with conventional commits**: Use [Conventional Commits](https://www.conventionalcommits.org/) format for your commits:
|
|
415
|
+
- `feat:` for new features (minor version bump)
|
|
416
|
+
- `fix:` for bug fixes (patch version bump)
|
|
417
|
+
- `feat!:` or `BREAKING CHANGE:` for breaking changes (major version bump)
|
|
418
|
+
- `chore:`, `docs:`, `test:`, etc. for non-release commits
|
|
419
|
+
|
|
420
|
+
2. **Release PR is created automatically**: When you push to `main`, release-please creates/updates a release PR with:
|
|
421
|
+
- Version bump in `package.json`
|
|
422
|
+
- Updated `CHANGELOG.md`
|
|
423
|
+
- Release notes
|
|
424
|
+
|
|
425
|
+
3. **Merge the release PR**: When you're ready to release, merge the PR. This will:
|
|
426
|
+
- Create a GitHub release and git tag
|
|
427
|
+
- Automatically publish to npm with provenance (using trusted publishing, no secrets needed)
|
|
428
|
+
|
|
429
|
+
**Example commits:**
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
git commit -m "feat: add support for view operations"
|
|
433
|
+
git commit -m "fix: handle empty namespace list correctly"
|
|
434
|
+
git commit -m "feat!: change auth config structure"
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Contributing
|
|
438
|
+
|
|
439
|
+
Contributions are welcome! This library aims to be a minimal, generic client for the Iceberg REST Catalog API.
|