@xylex-group/athena 2.6.0 → 2.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.
Files changed (44) hide show
  1. package/README.md +210 -17
  2. package/dist/browser.cjs +2102 -527
  3. package/dist/browser.cjs.map +1 -1
  4. package/dist/browser.d.cts +8 -7
  5. package/dist/browser.d.ts +8 -7
  6. package/dist/browser.js +2096 -528
  7. package/dist/browser.js.map +1 -1
  8. package/dist/cli/index.cjs +2116 -559
  9. package/dist/cli/index.cjs.map +1 -1
  10. package/dist/cli/index.d.cts +3 -3
  11. package/dist/cli/index.d.ts +3 -3
  12. package/dist/cli/index.js +2116 -559
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/cookies.cjs +10 -3
  15. package/dist/cookies.cjs.map +1 -1
  16. package/dist/cookies.js +10 -3
  17. package/dist/cookies.js.map +1 -1
  18. package/dist/index.cjs +2694 -748
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +8 -7
  21. package/dist/index.d.ts +8 -7
  22. package/dist/index.js +2688 -749
  23. package/dist/index.js.map +1 -1
  24. package/dist/model-form-DMed05gE.d.cts +3037 -0
  25. package/dist/model-form-DXPlOnlI.d.ts +3037 -0
  26. package/dist/{pipeline-Ce3pTw5h.d.ts → pipeline-CkMnhwPI.d.ts} +1 -1
  27. package/dist/{pipeline-D1ZYeoH7.d.cts → pipeline-D4sJRKqN.d.cts} +1 -1
  28. package/dist/react-email-DZhDDlEl.d.cts +417 -0
  29. package/dist/react-email-Lrz9A-BW.d.ts +417 -0
  30. package/dist/react.cjs +178 -71
  31. package/dist/react.cjs.map +1 -1
  32. package/dist/react.d.cts +22 -5
  33. package/dist/react.d.ts +22 -5
  34. package/dist/react.js +92 -5
  35. package/dist/react.js.map +1 -1
  36. package/dist/{types-CUuo4NDi.d.cts → types-BzY6fETM.d.ts} +47 -5
  37. package/dist/{types-DapchQY5.d.ts → types-CAtTGGoz.d.cts} +47 -5
  38. package/dist/{types-DSX6AT5B.d.cts → types-vikz9YIO.d.cts} +23 -1
  39. package/dist/{types-DSX6AT5B.d.ts → types-vikz9YIO.d.ts} +23 -1
  40. package/package.json +194 -193
  41. package/dist/model-form-BaHWi3gm.d.cts +0 -1383
  42. package/dist/model-form-Dh6gWjL0.d.ts +0 -1383
  43. package/dist/react-email-B8O1Jeff.d.cts +0 -1230
  44. package/dist/react-email-CDEF0jij.d.ts +0 -1230
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # athena-js
2
2
 
3
- current version: `2.6.0`
3
+ current version: `2.8.0`
4
4
  `@xylex-group/athena` is a database driver and API gateway SDK that lets you interact with SQL backends over HTTP through a fluent builder API. It ships a typed query builder for Node.js / server environments plus Athena-native React hooks for client-side use.
5
5
 
6
6
  ## Install
@@ -24,10 +24,14 @@ npm install react # React >=17 required for the hook
24
24
  ```ts
25
25
  import { createClient } from "@xylex-group/athena";
26
26
 
27
- const athenaClient = createClient(ATHENA_URL, ATHENA_API_KEY, {
28
- client: "CLIENT_NAME",
29
- backend: { type: "athena" },
30
- });
27
+ const athenaClient = createClient(
28
+ ATHENA_URL,
29
+ ATHENA_API_KEY,
30
+ {
31
+ client: "CLIENT_NAME",
32
+ backend: { type: "athena" },
33
+ },
34
+ );
31
35
 
32
36
  const { data, error } = await athenaClient.from("orchestral_sections").findMany({
33
37
  select: {
@@ -47,6 +51,34 @@ if (error) {
47
51
  }
48
52
  ```
49
53
 
54
+ `createClient(url, key)` is the canonical SDK shape. The SDK treats `url` as the public unified Athena base URL and derives:
55
+
56
+ - DB: `${url}/db`
57
+ - Auth: `${url}/auth`
58
+ - Storage: `${url}/storage`
59
+
60
+ You can still override individual services when needed:
61
+
62
+ ```ts
63
+ const athena = createClient({
64
+ key: ATHENA_API_KEY,
65
+ db: { url: process.env.ATHENA_DB_URL },
66
+ auth: { url: process.env.ATHENA_AUTH_URL },
67
+ storage: { url: process.env.ATHENA_STORAGE_URL },
68
+ });
69
+ ```
70
+
71
+ Legacy top-level aliases are also supported:
72
+
73
+ ```ts
74
+ const athena = createClient({
75
+ key: ATHENA_API_KEY,
76
+ gatewayUrl: process.env.ATHENA_DB_URL,
77
+ authUrl: process.env.ATHENA_AUTH_URL,
78
+ storageUrl: process.env.ATHENA_STORAGE_URL,
79
+ });
80
+ ```
81
+
50
82
  Example version baseline: SDK `@xylex-group/athena` `2.4.0`, Athena server `3.12.3` verified on 2026-06-04.
51
83
 
52
84
  `.findMany({ select, where, orderBy, limit })` is the clean canonical read surface.
@@ -238,28 +270,38 @@ It also supports dynamic `baseURL` host resolution plus static/dynamic
238
270
 
239
271
  For the full details and current scope, see [`docs/auth/server-bootstrap.mdx`](docs/auth/server-bootstrap.mdx).
240
272
 
241
- ### Typed schema registry (model-first)
273
+ ### Typed schema registry (table-first)
242
274
 
243
- You can keep `createClient(...).from<T>(...)` as-is, or opt into a typed registry:
275
+ You can keep `createClient(...).from<T>(...)` as-is, or opt into a typed registry with the new Zero-style table DSL:
244
276
 
245
277
  ```ts
246
278
  import {
279
+ boolean,
247
280
  createTypedClient,
248
281
  defineDatabase,
249
- defineModel,
250
282
  defineRegistry,
251
283
  defineSchema,
284
+ enumeration,
285
+ json,
286
+ string,
287
+ table,
252
288
  } from "@xylex-group/athena";
253
289
 
290
+ const users = table("users")
291
+ .schema("public")
292
+ .columns({
293
+ id: string().generated(),
294
+ email: string(),
295
+ active: boolean().defaulted(),
296
+ mood: enumeration(["happy", "sad"] as const).optional(),
297
+ settings: json<{ theme: "light" | "dark" }>(),
298
+ })
299
+ .primaryKey("id");
300
+
254
301
  const registry = defineRegistry({
255
302
  primary: defineDatabase({
256
303
  public: defineSchema({
257
- users: defineModel<{ id: string; email: string }>({
258
- meta: {
259
- primaryKey: ["id"],
260
- nullable: { id: false, email: false },
261
- },
262
- }),
304
+ users,
263
305
  }),
264
306
  }),
265
307
  });
@@ -274,8 +316,45 @@ await typed
274
316
  .withTenantContext({ organizationId: "org_1" })
275
317
  .fromModel("primary", "public", "users")
276
318
  .select("*");
319
+
320
+ const insert = users.schemas.form.parse({
321
+ email: "ada@example.com",
322
+ mood: "",
323
+ settings: { theme: "light" },
324
+ });
277
325
  ```
278
326
 
327
+ You can also pass that native Athena table/model value directly into the root client to avoid repeating the string table target:
328
+
329
+ ```ts
330
+ const result = await athena.from(users)
331
+ .select("id, email, active")
332
+ .eq("active", true)
333
+ .order("created_at", { ascending: false })
334
+ .limit(25);
335
+ ```
336
+
337
+ This is the viable opt-in short form because the `users` value carries runtime target metadata. A generic-only call like `from<UserPublicSchema>()` cannot resolve a table at runtime after TypeScript erases types.
338
+
339
+ If you want compile-time validation for simple string selects and RPC column names, enable the experimental strict mode:
340
+
341
+ ```ts
342
+ const strictAthena = createClient(ATHENA_URL, ATHENA_API_KEY, {
343
+ experimental: {
344
+ typecheckColumns: true,
345
+ },
346
+ });
347
+
348
+ await strictAthena.from(users).select("id, email").order("created_at");
349
+
350
+ // compile-time error
351
+ strictAthena.from(users).select("id, missing_column");
352
+ ```
353
+
354
+ For the DB helper surface, use `strictAthena.db.from<UserRow>("users").select("id, email")` when you want inline typed column validation. `strictAthena.db.select<UserRow>("users")` still gives a typed row-aware chain, but it does not accept inline typed column arguments.
355
+
356
+ `defineModel(...)` remains fully supported for compatibility and manual contracts.
357
+
279
358
  For full details, see [`docs/typed-schema-registry.md`](./docs/typed-schema-registry.md).
280
359
 
281
360
  For exhaustive method-by-method documentation with usage snippets (root client, runtime builders, auth bindings, react runtime, cookies, and utils), see [`docs/complete-method-reference.md`](./docs/complete-method-reference.md).
@@ -294,15 +373,91 @@ athena-js generate --help
294
373
  athena-js help generate
295
374
  ```
296
375
 
376
+ Out of the box, `athena-js generate` now works without an `athena.config.*` file in the common cases:
377
+
378
+ ```bash
379
+ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db athena-js generate --dry-run
380
+ ```
381
+
382
+ ```bash
383
+ ATHENA_URL=https://athena-db.com ATHENA_API_KEY=secret ATHENA_GENERATOR_DB=app_db athena-js generate --dry-run
384
+ ```
385
+
386
+ Smallest direct-mode config file:
387
+
388
+ ```ts
389
+ import { defineGeneratorConfig } from "@xylex-group/athena";
390
+
391
+ export default defineGeneratorConfig({
392
+ provider: {
393
+ kind: "postgres",
394
+ mode: "direct",
395
+ },
396
+ });
397
+ ```
398
+
399
+ Smallest table-builder config:
400
+
401
+ ```ts
402
+ import { defineGeneratorConfig } from "@xylex-group/athena";
403
+
404
+ export default defineGeneratorConfig({
405
+ provider: {
406
+ kind: "postgres",
407
+ mode: "direct",
408
+ },
409
+ output: {
410
+ format: "table-builder",
411
+ },
412
+ });
413
+ ```
414
+
415
+ Common copy-paste starts:
416
+
417
+ ```bash
418
+ # direct postgres, no config file
419
+ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db athena-js generate --dry-run
420
+
421
+ # direct postgres + Zero-style output + multiple schemas
422
+ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db \
423
+ ATHENA_GENERATOR_OUTPUT_FORMAT=table-builder \
424
+ ATHENA_GENERATOR_SCHEMAS=public,analytics \
425
+ athena-js generate --dry-run
426
+
427
+ # gateway-only CI job, no config file
428
+ ATHENA_URL=https://athena-db.com \
429
+ ATHENA_API_KEY=secret \
430
+ ATHENA_GENERATOR_DB=app_db \
431
+ athena-js generate --dry-run
432
+ ```
433
+
434
+ Smallest gateway table-builder config:
435
+
436
+ ```ts
437
+ import { defineGeneratorConfig } from "@xylex-group/athena";
438
+
439
+ export default defineGeneratorConfig({
440
+ provider: {
441
+ kind: "postgres",
442
+ mode: "gateway",
443
+ },
444
+ output: {
445
+ format: "table-builder",
446
+ },
447
+ });
448
+ ```
449
+
297
450
  Generator supports:
298
451
 
299
452
  - PostgreSQL direct introspection (`provider.mode = "direct"`, `provider.connectionString` from your `PG_URL`/`DATABASE_URL`)
300
453
  - PostgreSQL gateway-only introspection (`provider.mode = "gateway"` via Athena `POST /gateway/query`)
301
454
  - Multiple schema syncs such as `public` plus `athena`, with schema-safe default output paths
455
+ - Two output formats: legacy `define-model` artifacts (default) or the new Zero-style `table-builder` format via `output.format`
302
456
  - Placeholder-driven output paths
303
457
  - Feature flags (`features.emitRegistry`, `features.emitRelations`)
304
458
  - Typed env-backed config fields via `generatorEnv(...)` for connection strings, schema lists, naming styles, flags, and placeholder maps
305
459
 
460
+ For copy-paste quickstarts and more example profiles, see [`docs/generator-quickstart.md`](./docs/generator-quickstart.md).
306
461
  For full generator configuration and troubleshooting, see [`docs/generator-config.md`](./docs/generator-config.md).
307
462
  For full CLI commands, help behavior, and troubleshooting, see [`docs/cli-command-reference.md`](./docs/cli-command-reference.md).
308
463
  For CI/CD pipelines and generated-file branch policy, see [`docs/generator-cicd.md`](./docs/generator-cicd.md).
@@ -375,7 +530,7 @@ requireAffected(inserted, { min: 1 }, { table: "users", operation: "insert" });
375
530
  ### Structured errors by default
376
531
 
377
532
  ```ts
378
- import { createClient, normalizeAthenaError } from "@xylex-group/athena";
533
+ import { createClient } from "@xylex-group/athena";
379
534
 
380
535
  const athena = createClient(ATHENA_URL, ATHENA_API_KEY);
381
536
 
@@ -391,13 +546,13 @@ if (error) {
391
546
 
392
547
  `result.error` already carries normalized `kind` values (`unique_violation`, `validation`, `auth`, `rate_limit`, `transient`, etc.) plus operation metadata.
393
548
 
394
- `normalizeAthenaError(result)` still exists when you need the normalized envelope from an arbitrary thrown value or mixed unknown input.
549
+ `normalizeAthenaError(...)` is deprecated. Prefer `result.error` on failed results and the structured fields already attached to thrown SDK errors.
395
550
 
396
551
  ### Query tracing (experimental)
397
552
 
398
553
  ```ts
399
554
  const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
400
- experimental: { traceQueries: true },
555
+ experimental: { traceQueries: true, debugAst: true },
401
556
  });
402
557
  ```
403
558
 
@@ -426,6 +581,44 @@ const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
426
581
  });
427
582
  ```
428
583
 
584
+ If you also enable `debugAst: true`, every traced operation includes a normalized AST, and successful results expose the same AST through `getAthenaDebugAst(...)`:
585
+
586
+ ```ts
587
+ import { createClient, getAthenaDebugAst } from "@xylex-group/athena";
588
+
589
+ const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
590
+ experimental: {
591
+ debugAst: true,
592
+ },
593
+ });
594
+
595
+ const result = await athena.from("users").eq("id", 1).select("id");
596
+ const ast = getAthenaDebugAst(result);
597
+ ```
598
+
599
+ This works across the runtime operation families too:
600
+
601
+ ```ts
602
+ const inserted = await athena
603
+ .from("users")
604
+ .insert({ email: "ada@example.com" })
605
+ .select("id,email");
606
+
607
+ const insertedAst = getAthenaDebugAst(inserted);
608
+
609
+ const rpcResult = await athena
610
+ .rpc("list_users", { role: "admin" })
611
+ .eq("active", true)
612
+ .select("id,email");
613
+
614
+ const rpcAst = getAthenaDebugAst(rpcResult);
615
+
616
+ const sqlResult = await athena.query<{ id: number }>("select id from users where active = true");
617
+ const sqlAst = getAthenaDebugAst(sqlResult);
618
+ ```
619
+
620
+ If `traceQueries` is enabled too, the same normalized AST is emitted on each `AthenaQueryTraceEvent.ast`.
621
+
429
622
  ### Read retries (experimental)
430
623
 
431
624
  ```ts