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 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
+ [![CI](https://github.com/supabase/iceberg-js/actions/workflows/ci.yml/badge.svg)](https://github.com/supabase/iceberg-js/actions/workflows/ci.yml)
4
+ [![npm version](https://badge.fury.io/js/iceberg-js.svg)](https://www.npmjs.com/package/iceberg-js)
5
+ [![pkg.pr.new](https://pkg.pr.new/badge/supabase/iceberg-js)](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.