@xylex-group/athena 2.7.0 → 2.8.2

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 (36) hide show
  1. package/README.md +218 -18
  2. package/dist/browser.cjs +2173 -675
  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 +2167 -676
  7. package/dist/browser.js.map +1 -1
  8. package/dist/cli/index.cjs +2068 -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 +2068 -559
  13. package/dist/cli/index.js.map +1 -1
  14. package/dist/index.cjs +2815 -945
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +8 -7
  17. package/dist/index.d.ts +8 -7
  18. package/dist/index.js +2809 -946
  19. package/dist/index.js.map +1 -1
  20. package/dist/{model-form-AKYrgede.d.ts → model-form-Cx3wtvi8.d.ts} +963 -93
  21. package/dist/{model-form-ehfqLuG7.d.cts → model-form-_ugfOXao.d.cts} +963 -93
  22. package/dist/{pipeline-BfCWSRYl.d.cts → pipeline-BtD-Uo5X.d.cts} +1 -1
  23. package/dist/{pipeline-BUsR9XlO.d.ts → pipeline-yCIZNJHE.d.ts} +1 -1
  24. package/dist/{react-email-BQzmXBDE.d.cts → react-email-CiiSVa9F.d.cts} +121 -10
  25. package/dist/{react-email-BrVRp80B.d.ts → react-email-WN8UU3AL.d.ts} +121 -10
  26. package/dist/react.cjs +1 -1
  27. package/dist/react.cjs.map +1 -1
  28. package/dist/react.d.cts +4 -4
  29. package/dist/react.d.ts +4 -4
  30. package/dist/react.js +1 -1
  31. package/dist/react.js.map +1 -1
  32. package/dist/{types-t_TVqnmp.d.ts → types-89EfjLjV.d.cts} +48 -5
  33. package/dist/{types-BSIsyss1.d.cts → types-C2kiTt6-.d.ts} +48 -5
  34. package/dist/{types-BsyRW49r.d.cts → types-g8G6J0xE.d.cts} +26 -1
  35. package/dist/{types-BsyRW49r.d.ts → types-g8G6J0xE.d.ts} +26 -1
  36. package/package.json +28 -57
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # athena-js
2
2
 
3
- current version: `2.7.0`
3
+ current version: `2.8.2`
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
+ });
325
+ ```
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);
277
335
  ```
278
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,98 @@ 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
+ Important:
416
+
417
+ - `output.format = "table-builder"` is stable generator behavior, not an experimental flag.
418
+ - `experimental.findManyAst` is a separate runtime transport opt-in for `findMany(...)`; it does not enable generated table artifacts.
419
+ - the default generator mode is still legacy `define-model`, and the default model target is still schema-scoped (`athena/models/{schema_kebab}/{model_kebab}.ts`)
420
+ - if you want flat `athena/models/*.ts` files, set `output.targets.model = "athena/models/{model_kebab}.ts"` (or `ATHENA_GENERATOR_MODEL_TARGET=athena/models/{model_kebab}.ts`); multi-schema collisions are still auto-scoped by schema when needed
421
+
422
+ Common copy-paste starts:
423
+
424
+ ```bash
425
+ # direct postgres, no config file
426
+ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db athena-js generate --dry-run
427
+
428
+ # direct postgres + Zero-style output + multiple schemas
429
+ DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/app_db \
430
+ ATHENA_GENERATOR_OUTPUT_FORMAT=table-builder \
431
+ ATHENA_GENERATOR_SCHEMAS=public,analytics \
432
+ athena-js generate --dry-run
433
+
434
+ # gateway-only CI job, no config file
435
+ ATHENA_URL=https://athena-db.com \
436
+ ATHENA_API_KEY=secret \
437
+ ATHENA_GENERATOR_DB=app_db \
438
+ athena-js generate --dry-run
439
+ ```
440
+
441
+ Smallest gateway table-builder config:
442
+
443
+ ```ts
444
+ import { defineGeneratorConfig } from "@xylex-group/athena";
445
+
446
+ export default defineGeneratorConfig({
447
+ provider: {
448
+ kind: "postgres",
449
+ mode: "gateway",
450
+ },
451
+ output: {
452
+ format: "table-builder",
453
+ },
454
+ });
455
+ ```
456
+
297
457
  Generator supports:
298
458
 
299
459
  - PostgreSQL direct introspection (`provider.mode = "direct"`, `provider.connectionString` from your `PG_URL`/`DATABASE_URL`)
300
460
  - PostgreSQL gateway-only introspection (`provider.mode = "gateway"` via Athena `POST /gateway/query`)
301
- - Multiple schema syncs such as `public` plus `athena`, with schema-safe default output paths
461
+ - Multiple schema syncs such as `public` plus `athena`, with schema-safe default output paths
462
+ - Two output formats: legacy `define-model` artifacts (default) or the new Zero-style `table-builder` format via `output.format`
302
463
  - Placeholder-driven output paths
303
464
  - Feature flags (`features.emitRegistry`, `features.emitRelations`)
304
465
  - Typed env-backed config fields via `generatorEnv(...)` for connection strings, schema lists, naming styles, flags, and placeholder maps
305
466
 
467
+ For copy-paste quickstarts and more example profiles, see [`docs/generator-quickstart.md`](./docs/generator-quickstart.md).
306
468
  For full generator configuration and troubleshooting, see [`docs/generator-config.md`](./docs/generator-config.md).
307
469
  For full CLI commands, help behavior, and troubleshooting, see [`docs/cli-command-reference.md`](./docs/cli-command-reference.md).
308
470
  For CI/CD pipelines and generated-file branch policy, see [`docs/generator-cicd.md`](./docs/generator-cicd.md).
@@ -375,7 +537,7 @@ requireAffected(inserted, { min: 1 }, { table: "users", operation: "insert" });
375
537
  ### Structured errors by default
376
538
 
377
539
  ```ts
378
- import { createClient, normalizeAthenaError } from "@xylex-group/athena";
540
+ import { createClient } from "@xylex-group/athena";
379
541
 
380
542
  const athena = createClient(ATHENA_URL, ATHENA_API_KEY);
381
543
 
@@ -391,13 +553,13 @@ if (error) {
391
553
 
392
554
  `result.error` already carries normalized `kind` values (`unique_violation`, `validation`, `auth`, `rate_limit`, `transient`, etc.) plus operation metadata.
393
555
 
394
- `normalizeAthenaError(result)` still exists when you need the normalized envelope from an arbitrary thrown value or mixed unknown input.
556
+ `normalizeAthenaError(...)` is deprecated. Prefer `result.error` on failed results and the structured fields already attached to thrown SDK errors.
395
557
 
396
558
  ### Query tracing (experimental)
397
559
 
398
560
  ```ts
399
561
  const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
400
- experimental: { traceQueries: true },
562
+ experimental: { traceQueries: true, debugAst: true },
401
563
  });
402
564
  ```
403
565
 
@@ -426,6 +588,44 @@ const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
426
588
  });
427
589
  ```
428
590
 
591
+ If you also enable `debugAst: true`, every traced operation includes a normalized AST, and successful results expose the same AST through `getAthenaDebugAst(...)`:
592
+
593
+ ```ts
594
+ import { createClient, getAthenaDebugAst } from "@xylex-group/athena";
595
+
596
+ const athena = createClient(ATHENA_URL, ATHENA_API_KEY, {
597
+ experimental: {
598
+ debugAst: true,
599
+ },
600
+ });
601
+
602
+ const result = await athena.from("users").eq("id", 1).select("id");
603
+ const ast = getAthenaDebugAst(result);
604
+ ```
605
+
606
+ This works across the runtime operation families too:
607
+
608
+ ```ts
609
+ const inserted = await athena
610
+ .from("users")
611
+ .insert({ email: "ada@example.com" })
612
+ .select("id,email");
613
+
614
+ const insertedAst = getAthenaDebugAst(inserted);
615
+
616
+ const rpcResult = await athena
617
+ .rpc("list_users", { role: "admin" })
618
+ .eq("active", true)
619
+ .select("id,email");
620
+
621
+ const rpcAst = getAthenaDebugAst(rpcResult);
622
+
623
+ const sqlResult = await athena.query<{ id: number }>("select id from users where active = true");
624
+ const sqlAst = getAthenaDebugAst(sqlResult);
625
+ ```
626
+
627
+ If `traceQueries` is enabled too, the same normalized AST is emitted on each `AthenaQueryTraceEvent.ast`.
628
+
429
629
  ### Read retries (experimental)
430
630
 
431
631
  ```ts