weifuwu 0.23.1 → 0.23.3

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/dist/index.js CHANGED
@@ -1619,22 +1619,6 @@ function compress(options) {
1619
1619
  }
1620
1620
 
1621
1621
  // helmet.ts
1622
- var DEFAULTS = {
1623
- contentSecurityPolicy: "default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests",
1624
- crossOriginEmbedderPolicy: "require-corp",
1625
- crossOriginOpenerPolicy: "same-origin",
1626
- crossOriginResourcePolicy: "same-origin",
1627
- originAgentCluster: "?1",
1628
- referrerPolicy: "no-referrer",
1629
- strictTransportSecurity: "max-age=15552000; includeSubDomains",
1630
- xContentTypeOptions: "nosniff",
1631
- xDnsPrefetchControl: "off",
1632
- xDownloadOptions: "noopen",
1633
- xFrameOptions: "SAMEORIGIN",
1634
- xPermittedCrossDomainPolicies: "none",
1635
- xXssProtection: "0",
1636
- permissionsPolicy: "camera=(),display-capture=(),fullscreen=(),geolocation=(),microphone=()"
1637
- };
1638
1622
  var HEADER_MAP = {
1639
1623
  "Content-Security-Policy": "contentSecurityPolicy",
1640
1624
  "Cross-Origin-Embedder-Policy": "crossOriginEmbedderPolicy",
@@ -1667,6 +1651,22 @@ function helmet(options) {
1667
1651
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h });
1668
1652
  };
1669
1653
  }
1654
+ var DEFAULTS = {
1655
+ contentSecurityPolicy: "default-src 'self';base-uri 'self';font-src 'self' https: data:;form-action 'self';frame-ancestors 'self';img-src 'self' data:;object-src 'none';script-src 'self';script-src-attr 'none';style-src 'self' https: 'unsafe-inline';upgrade-insecure-requests",
1656
+ crossOriginEmbedderPolicy: "require-corp",
1657
+ crossOriginOpenerPolicy: "same-origin",
1658
+ crossOriginResourcePolicy: "same-origin",
1659
+ originAgentCluster: "?1",
1660
+ referrerPolicy: "no-referrer",
1661
+ strictTransportSecurity: "max-age=15552000; includeSubDomains",
1662
+ xContentTypeOptions: "nosniff",
1663
+ xDnsPrefetchControl: "off",
1664
+ xDownloadOptions: "noopen",
1665
+ xFrameOptions: "SAMEORIGIN",
1666
+ xPermittedCrossDomainPolicies: "none",
1667
+ xXssProtection: "0",
1668
+ permissionsPolicy: "camera=(),display-capture=(),fullscreen=(),geolocation=(),microphone=()"
1669
+ };
1670
1670
 
1671
1671
  // request-id.ts
1672
1672
  import crypto2 from "node:crypto";
@@ -2441,7 +2441,7 @@ function aiProvider(options) {
2441
2441
  return result.embedding;
2442
2442
  },
2443
2443
  async embedMany(texts) {
2444
- const result = await aiEmbedMany({ model: this.embeddingModel(), value: texts });
2444
+ const result = await aiEmbedMany({ model: this.embeddingModel(), values: texts });
2445
2445
  return result.embeddings;
2446
2446
  },
2447
2447
  generateText(params) {
@@ -2474,12 +2474,15 @@ import postgresFactory from "postgres";
2474
2474
 
2475
2475
  // postgres/schema/sql.ts
2476
2476
  var SQL = class {
2477
+ /** Template string parts (interleaved with values). */
2477
2478
  strings;
2479
+ /** Bound parameter values. */
2478
2480
  values;
2479
2481
  constructor(strings, values) {
2480
2482
  this.strings = strings;
2481
2483
  this.values = values;
2482
2484
  }
2485
+ /** Serialize to a raw SQL string (interpolating values inline for DDL use). */
2483
2486
  toSQL() {
2484
2487
  let result = "";
2485
2488
  for (let i = 0; i < this.strings.length; i++) {
@@ -2497,31 +2500,43 @@ function sql(strings, ...values) {
2497
2500
 
2498
2501
  // postgres/schema/columns.ts
2499
2502
  var ColumnBuilder = class {
2503
+ /** Column name. */
2500
2504
  name;
2505
+ /** SQL type string (e.g. `'TEXT'`, `'INTEGER'`). */
2501
2506
  sqlType;
2507
+ /** Whether this column is PRIMARY KEY. */
2502
2508
  isPrimaryKey = false;
2509
+ /** Whether this column allows NULL. */
2503
2510
  isNullable = true;
2511
+ /** Whether this column has a UNIQUE constraint. */
2504
2512
  isUnique = false;
2513
+ /** Whether the value is auto-generated (e.g. SERIAL, UUID defaults). */
2505
2514
  isAutoGenerate = false;
2515
+ /** DEFAULT expression as a raw SQL string. */
2506
2516
  defaultExpr = null;
2517
+ /** Foreign key reference, if any. */
2507
2518
  ref = null;
2508
2519
  constructor(name, sqlType) {
2509
2520
  this.name = name;
2510
2521
  this.sqlType = sqlType;
2511
2522
  }
2523
+ /** Mark as PRIMARY KEY (implies NOT NULL). */
2512
2524
  primaryKey() {
2513
2525
  this.isPrimaryKey = true;
2514
2526
  this.isNullable = false;
2515
2527
  return this;
2516
2528
  }
2529
+ /** Add NOT NULL constraint. */
2517
2530
  notNull() {
2518
2531
  this.isNullable = false;
2519
2532
  return this;
2520
2533
  }
2534
+ /** Allow NULL values (default). */
2521
2535
  nullable() {
2522
2536
  this.isNullable = true;
2523
2537
  return this;
2524
2538
  }
2539
+ /** Set a DEFAULT value. Accepts raw SQL, string, number, or boolean. */
2525
2540
  default(expr) {
2526
2541
  if (expr instanceof SQL) {
2527
2542
  this.defaultExpr = expr.toSQL();
@@ -2532,10 +2547,12 @@ var ColumnBuilder = class {
2532
2547
  }
2533
2548
  return this;
2534
2549
  }
2550
+ /** Add UNIQUE constraint. */
2535
2551
  unique() {
2536
2552
  this.isUnique = true;
2537
2553
  return this;
2538
2554
  }
2555
+ /** Add FOREIGN KEY reference to another table. */
2539
2556
  references(table, column = "id", onDelete) {
2540
2557
  this.ref = { table, column, onDelete };
2541
2558
  return this;
@@ -2629,8 +2646,11 @@ function and(...conditions) {
2629
2646
 
2630
2647
  // postgres/schema/table.ts
2631
2648
  var Table = class {
2649
+ /** Database table name. */
2632
2650
  tableName;
2651
+ /** All column builders (order-preserving). */
2633
2652
  columns;
2653
+ /** Column builders keyed by property name. */
2634
2654
  builders;
2635
2655
  colEntries;
2636
2656
  constructor(tableName, builders) {
@@ -2644,10 +2664,14 @@ var Table = class {
2644
2664
  column: col2
2645
2665
  }));
2646
2666
  }
2667
+ /** Check if the table has a column with the given DB name. */
2647
2668
  hasColumn(dbName) {
2648
2669
  return this.colEntries.some((e) => e.db === dbName);
2649
2670
  }
2650
- /** Returns a BoundTable pre-bound to the given sql connection (no need to pass sql to every method). */
2671
+ /**
2672
+ * Bind this table schema to a SQL connection, returning a `BoundTable`
2673
+ * that can run queries without passing `sql` to every call.
2674
+ */
2651
2675
  bind(sql2) {
2652
2676
  return new BoundTable(sql2, this.tableName, this.builders);
2653
2677
  }
@@ -3772,6 +3796,18 @@ function user(options) {
3772
3796
  return null;
3773
3797
  }
3774
3798
  }
3799
+ function extractToken(req, cookieName) {
3800
+ const header = req.headers.get("Authorization");
3801
+ if (header?.startsWith("Bearer ")) return header.slice(7);
3802
+ if (cookieName) {
3803
+ const cookies = req.headers.get("cookie")?.split(";").map((c) => c.trim()).filter(Boolean) || [];
3804
+ for (const c of cookies) {
3805
+ const eq2 = c.indexOf("=");
3806
+ if (eq2 > 0 && c.slice(0, eq2) === cookieName) return c.slice(eq2 + 1);
3807
+ }
3808
+ }
3809
+ return null;
3810
+ }
3775
3811
  function middleware() {
3776
3812
  return async (req, ctx, next) => {
3777
3813
  const sessionUserId = ctx.session?.userId;
@@ -3788,8 +3824,7 @@ function user(options) {
3788
3824
  delete ctx.session?.userId;
3789
3825
  }
3790
3826
  }
3791
- const header = req.headers.get("Authorization");
3792
- const token = header?.startsWith("Bearer ") ? header.slice(7) : null;
3827
+ const token = extractToken(req);
3793
3828
  if (token) {
3794
3829
  const userData = await verify(token);
3795
3830
  if (userData) {
@@ -3800,11 +3835,36 @@ function user(options) {
3800
3835
  return new Response("Unauthorized", { status: 401, headers: { "WWW-Authenticate": "Bearer" } });
3801
3836
  };
3802
3837
  }
3838
+ function middlewareOptional(opts) {
3839
+ const cookieName = opts?.cookie;
3840
+ return async (req, ctx, next) => {
3841
+ const token = extractToken(req, cookieName);
3842
+ if (token) {
3843
+ const userData = await verify(token);
3844
+ if (userData) {
3845
+ ctx.user = userData;
3846
+ }
3847
+ }
3848
+ return next(req, ctx);
3849
+ };
3850
+ }
3851
+ async function parseBody2(req) {
3852
+ const ct = req.headers.get("content-type") || "";
3853
+ if (ct.includes("application/json")) {
3854
+ return req.json();
3855
+ }
3856
+ const form = await req.formData();
3857
+ const obj = {};
3858
+ for (const [key, val] of form) {
3859
+ obj[key] = val;
3860
+ }
3861
+ return obj;
3862
+ }
3803
3863
  function router() {
3804
3864
  const r2 = new Router();
3805
3865
  r2.post("/register", async (req) => {
3806
3866
  try {
3807
- const body = await req.json();
3867
+ const body = await parseBody2(req);
3808
3868
  const result = await register(body);
3809
3869
  return Response.json(result, { status: 201 });
3810
3870
  } catch (err) {
@@ -3817,7 +3877,7 @@ function user(options) {
3817
3877
  });
3818
3878
  r2.post("/login", async (req, ctx) => {
3819
3879
  try {
3820
- const body = await req.json();
3880
+ const body = await parseBody2(req);
3821
3881
  const result = await login(body);
3822
3882
  if (ctx.session) {
3823
3883
  ;
@@ -3861,6 +3921,7 @@ function user(options) {
3861
3921
  }
3862
3922
  const mod = r;
3863
3923
  mod.middleware = middleware;
3924
+ mod.middlewareOptional = middlewareOptional;
3864
3925
  mod.migrate = migrate;
3865
3926
  mod.register = register;
3866
3927
  mod.login = login;
@@ -3993,7 +4054,7 @@ function attachCron(q, handlers) {
3993
4054
  function createMemoryQueue(opts) {
3994
4055
  const pollInterval = opts?.pollInterval ?? 200;
3995
4056
  const handlers = /* @__PURE__ */ new Map();
3996
- const jobs = [];
4057
+ const pending = [];
3997
4058
  const failed = [];
3998
4059
  const MAX_FAILED = 1e3;
3999
4060
  let running = false;
@@ -4004,8 +4065,8 @@ function createMemoryQueue(opts) {
4004
4065
  const MAX_CONCURRENT = 16;
4005
4066
  function insertJob(job) {
4006
4067
  let i = 0;
4007
- while (i < jobs.length && jobs[i].runAt <= job.runAt) i++;
4008
- jobs.splice(i, 0, job);
4068
+ while (i < pending.length && pending[i].runAt <= job.runAt) i++;
4069
+ pending.splice(i, 0, job);
4009
4070
  }
4010
4071
  async function execute(job, handler) {
4011
4072
  inflight++;
@@ -4030,8 +4091,8 @@ function createMemoryQueue(opts) {
4030
4091
  async function poll() {
4031
4092
  if (!running) return;
4032
4093
  const now = Date.now();
4033
- while (running && inflight < MAX_CONCURRENT && jobs.length > 0 && jobs[0].runAt <= now) {
4034
- const job = jobs.shift();
4094
+ while (running && inflight < MAX_CONCURRENT && pending.length > 0 && pending[0].runAt <= now) {
4095
+ const job = pending.shift();
4035
4096
  const handler = handlers.get(job.type);
4036
4097
  if (handler) execute(job, handler);
4037
4098
  }
@@ -4081,8 +4142,8 @@ function createMemoryQueue(opts) {
4081
4142
  mw.stop();
4082
4143
  while (inflight > 0) await new Promise((r) => setTimeout(r, 50));
4083
4144
  };
4084
- mw.jobs = async function jobs2(limit) {
4085
- return jobs2.slice(0, limit ?? 50);
4145
+ mw.jobs = async function(limit) {
4146
+ return pending.slice(0, limit ?? 50);
4086
4147
  };
4087
4148
  mw.failedJobs = async function failedJobs(limit) {
4088
4149
  return failed.slice(0, limit ?? 50);
@@ -5626,7 +5687,7 @@ function createRunner(deps) {
5626
5687
  }
5627
5688
  }
5628
5689
  async function addKnowledge(agentId, title, content) {
5629
- const chunks = chunkContent(content);
5690
+ const chunks = chunkContent(content, 1024, 128);
5630
5691
  const [first] = chunks;
5631
5692
  const embedding = await provider.embed(first);
5632
5693
  const vec = `[${embedding.join(",")}]`;
@@ -7105,12 +7166,6 @@ function liveRouter(dir) {
7105
7166
  const r = new Router();
7106
7167
  compileVendorBundle().catch(() => {
7107
7168
  });
7108
- r.get("/__wfw/v/bundle", async () => {
7109
- const code = await compileVendorBundle();
7110
- return new Response(code, {
7111
- headers: { "content-type": "application/javascript; charset=utf-8" }
7112
- });
7113
- });
7114
7169
  r.get("/__wfw/h/:hash", async (req, ctx) => {
7115
7170
  const hash = ctx.params.hash.replace(/\.js$/i, "");
7116
7171
  const code = hotBundleCache.get(hash);
@@ -7553,11 +7608,11 @@ function ssr(opts) {
7553
7608
  if (existsSync4(join5(dir, "app", "globals.css"))) {
7554
7609
  r.use("/", tailwindRouter(dir));
7555
7610
  }
7611
+ let devWatcher;
7556
7612
  if (isDev2) {
7557
7613
  r.use("/", liveRouter(dir));
7558
7614
  r.ws("/__weifuwu/livereload", liveWs());
7559
- const watcher = liveWatcher(dir);
7560
- r.close = watcher.close;
7615
+ devWatcher = liveWatcher(dir);
7561
7616
  }
7562
7617
  r.all("/*", async (req, ctx) => {
7563
7618
  const prefix = ctx.mountPath || "";
@@ -7566,7 +7621,17 @@ function ssr(opts) {
7566
7621
  const segments = relativePath.split("/").filter(Boolean);
7567
7622
  const resolved = await resolveRoute(dir, segments, routeCache);
7568
7623
  if (!resolved) {
7569
- return isDev2 ? Response.json({ error: "Not Found", path: "/" + segments.join("/"), method: req.method }, { status: 404 }) : new Response("Not Found", { status: 404 });
7624
+ if (isDev2) {
7625
+ const pages = discoverRoutes(dir).map((p) => p.path).sort();
7626
+ return Response.json({
7627
+ error: "Not Found",
7628
+ path: "/" + segments.join("/"),
7629
+ method: req.method,
7630
+ hint: "Available SSR pages",
7631
+ pages
7632
+ }, { status: 404 });
7633
+ }
7634
+ return new Response("Not Found", { status: 404 });
7570
7635
  }
7571
7636
  const mws = [
7572
7637
  ...resolved.errorFiles.map((f) => errorBoundary(f)),
@@ -7578,6 +7643,7 @@ function ssr(opts) {
7578
7643
  });
7579
7644
  const mod = r;
7580
7645
  mod.pages = () => discoverRoutes(dir);
7646
+ if (devWatcher) mod.close = devWatcher.close.bind(devWatcher);
7581
7647
  return mod;
7582
7648
  }
7583
7649
 
@@ -8929,7 +8995,6 @@ function flash(options) {
8929
8995
  value = raw;
8930
8996
  }
8931
8997
  }
8932
- ;
8933
8998
  ctx.flash = {
8934
8999
  value,
8935
9000
  set: makeSetFlash(name, referer)
@@ -10490,8 +10555,7 @@ function session(options) {
10490
10555
  await store2.set(newId, data, ttl);
10491
10556
  await store2.destroy(loadedSid);
10492
10557
  loadedSid = newId;
10493
- currentSession[kId] = newId;
10494
- currentSession[kCreatedAt] = data[kCreatedAt];
10558
+ Object.assign(currentSession, { [kId]: newId, [kCreatedAt]: data[kCreatedAt] });
10495
10559
  }
10496
10560
  const currentData = isSessionActive(currentSession) ? JSON.stringify(currentSession) : null;
10497
10561
  const wasSaved = currentSession[kSaved];
@@ -11402,7 +11466,8 @@ function permissions(options) {
11402
11466
  });
11403
11467
  function requireRole(...roles) {
11404
11468
  return (req, ctx, next) => {
11405
- if (!ctx.permissions?.roles || !roles.some((r) => ctx.permissions.roles.has(r))) {
11469
+ const p = ctx;
11470
+ if (!p.permissions?.roles || !roles.some((r) => p.permissions.roles.has(r))) {
11406
11471
  return Response.json(
11407
11472
  { error: `Forbidden: requires one of roles [${roles.join(", ")}]` },
11408
11473
  { status: 403 }
@@ -11413,12 +11478,13 @@ function permissions(options) {
11413
11478
  }
11414
11479
  function requirePermission(...perms) {
11415
11480
  return (req, ctx, next) => {
11416
- const userPerms = ctx.permissions?.permissions;
11481
+ const p = ctx;
11482
+ const userPerms = p.permissions?.permissions;
11417
11483
  if (!userPerms) {
11418
11484
  return Response.json({ error: "Forbidden: no permissions loaded" }, { status: 403 });
11419
11485
  }
11420
11486
  if (userPerms.has("*")) return next(req, ctx);
11421
- const missing = perms.filter((p) => !userPerms.has(p));
11487
+ const missing = perms.filter((p2) => !userPerms.has(p2));
11422
11488
  if (missing.length > 0) {
11423
11489
  return Response.json(
11424
11490
  { error: `Forbidden: missing permissions [${missing.join(", ")}]` },
package/dist/mailer.d.ts CHANGED
@@ -1,20 +1,50 @@
1
1
  import type { Transporter } from 'nodemailer';
2
+ /** Options for sending an email. */
2
3
  export interface MailOptions {
4
+ /** Recipient address(es). */
3
5
  to: string | string[];
6
+ /** Email subject. */
4
7
  subject: string;
8
+ /** Plain text body. */
5
9
  text?: string;
10
+ /** HTML body. */
6
11
  html?: string;
12
+ /** Sender address (overrides `MailerOptions.from`). */
7
13
  from?: string;
14
+ /** CC recipient(s). */
8
15
  cc?: string | string[];
16
+ /** BCC recipient(s). */
9
17
  bcc?: string | string[];
10
18
  }
19
+ /** Options for {@link mailer}. */
11
20
  export interface MailerOptions {
21
+ /** Nodemailer transport string or pre-built transporter object. */
12
22
  transport?: string | Transporter;
23
+ /** Default sender address. */
13
24
  from?: string;
25
+ /** Custom send function (bypasses nodemailer). */
14
26
  send?: (opts: MailOptions) => Promise<void>;
15
27
  }
28
+ /** Mailer instance returned by {@link mailer}. */
16
29
  export interface Mailer {
30
+ /** Send an email. */
17
31
  send: (opts: MailOptions) => Promise<void>;
32
+ /** Close the nodemailer transport. */
18
33
  close: () => Promise<void>;
19
34
  }
35
+ /**
36
+ * Create a mailer instance.
37
+ *
38
+ * ```ts
39
+ * import { mailer } from 'weifuwu'
40
+ *
41
+ * const email = mailer({ transport: 'smtp://user:pass@smtp.example.com' })
42
+ * await email.send({
43
+ * to: 'user@example.com',
44
+ * subject: 'Hello',
45
+ * text: 'Hello from weifuwu!',
46
+ * })
47
+ * await email.close()
48
+ * ```
49
+ */
20
50
  export declare function mailer(options: MailerOptions): Mailer;
@@ -1,9 +1,11 @@
1
1
  import type { Sql } from './vendor.ts';
2
- import type { Middleware, Context } from './types.ts';
2
+ import type { Middleware, Context, Handler } from './types.ts';
3
3
  declare module './types.ts' {
4
4
  interface Context {
5
- roles: Set<string>;
6
- permissions: Set<string>;
5
+ permissions: {
6
+ roles: Set<string>;
7
+ permissions: Set<string>;
8
+ };
7
9
  }
8
10
  }
9
11
  export interface PermissionsOptions {
@@ -1,43 +1,99 @@
1
1
  import { SQL } from './sql.ts';
2
+ /** Reference to another table's column (foreign key). */
2
3
  export interface ColumnReference {
4
+ /** Referenced table name. */
3
5
  table: string;
6
+ /** Referenced column name (default: `'id'`). */
4
7
  column: string;
8
+ /** `ON DELETE` action (e.g. `'cascade'`, `'set null'`). */
5
9
  onDelete?: string;
6
10
  }
11
+ /**
12
+ * Fluent column builder for DDL generation.
13
+ *
14
+ * ```ts
15
+ * text('name').notNull().unique()
16
+ * integer('user_id').references('users')
17
+ * timestamptz('created_at').default(sql`NOW()`)
18
+ * ```
19
+ */
7
20
  export declare class ColumnBuilder<T> {
21
+ /** Column name. */
8
22
  name: string;
23
+ /** SQL type string (e.g. `'TEXT'`, `'INTEGER'`). */
9
24
  sqlType: string;
25
+ /** Whether this column is PRIMARY KEY. */
10
26
  isPrimaryKey: boolean;
27
+ /** Whether this column allows NULL. */
11
28
  isNullable: boolean;
29
+ /** Whether this column has a UNIQUE constraint. */
12
30
  isUnique: boolean;
31
+ /** Whether the value is auto-generated (e.g. SERIAL, UUID defaults). */
13
32
  isAutoGenerate: boolean;
33
+ /** DEFAULT expression as a raw SQL string. */
14
34
  defaultExpr: string | null;
35
+ /** Foreign key reference, if any. */
15
36
  ref: ColumnReference | null;
16
37
  constructor(name: string, sqlType: string);
38
+ /** Mark as PRIMARY KEY (implies NOT NULL). */
17
39
  primaryKey(): this;
40
+ /** Add NOT NULL constraint. */
18
41
  notNull(): this;
42
+ /** Allow NULL values (default). */
19
43
  nullable(): this;
44
+ /** Set a DEFAULT value. Accepts raw SQL, string, number, or boolean. */
20
45
  default(expr: SQL | string | number | boolean): this;
46
+ /** Add UNIQUE constraint. */
21
47
  unique(): this;
48
+ /** Add FOREIGN KEY reference to another table. */
22
49
  references(table: string, column?: string, onDelete?: string): this;
23
50
  }
51
+ /** Auto-incrementing integer primary key (`SERIAL`). */
24
52
  export declare function serial(name: string): ColumnBuilder<number>;
53
+ /** UUID column. */
25
54
  export declare function uuid(name: string): ColumnBuilder<string>;
55
+ /** TEXT column. */
26
56
  export declare function text(name: string): ColumnBuilder<string>;
57
+ /** INTEGER column. */
27
58
  export declare function integer(name: string): ColumnBuilder<number>;
59
+ /** BOOLEAN column (exported as `boolean`). */
28
60
  export declare function boolean_(name: string): ColumnBuilder<boolean>;
29
61
  export { boolean_ as boolean };
62
+ /** TIMESTAMPTZ column (timestamp with time zone). */
30
63
  export declare function timestamptz(name: string): ColumnBuilder<string>;
64
+ /** JSONB column (stores arbitrary JSON data). */
31
65
  export declare function jsonb<T = unknown>(name: string): ColumnBuilder<T>;
66
+ /** TEXT[] column (PostgreSQL array of text). */
32
67
  export declare function textArray(name: string): ColumnBuilder<string[]>;
68
+ /** Vector column for pgvector (embedding storage). Requires `dimensions`. */
33
69
  export declare function vector(name: string, dims: number): ColumnBuilder<number[]>;
34
70
  export interface PartitionByDef {
35
71
  type: 'RANGE' | 'LIST' | 'HASH';
36
72
  column: string;
37
73
  }
38
74
  export declare function partitionBy(type: 'range' | 'list' | 'hash', column: string): PartitionByDef;
75
+ /**
76
+ * Create a pair of `created_at` / `updated_at` timestamp columns
77
+ * that default to `NOW()` and are NOT NULL.
78
+ *
79
+ * ```ts
80
+ * pgTable('users', {
81
+ * id: serial('id').primaryKey(),
82
+ * name: text('name'),
83
+ * ...timestamps(),
84
+ * })
85
+ * ```
86
+ */
39
87
  export declare function timestamps(): {
40
88
  readonly created_at: ColumnBuilder<string>;
41
89
  readonly updated_at: ColumnBuilder<string>;
42
90
  };
91
+ /**
92
+ * Convert a ColumnBuilder into a DDL column definition string.
93
+ *
94
+ * ```ts
95
+ * toDDL(text('name').notNull())
96
+ * // '"name" TEXT NOT NULL'
97
+ * ```
98
+ */
43
99
  export declare function toDDL(col: ColumnBuilder<unknown>): string;
@@ -1,7 +1,22 @@
1
+ /**
2
+ * A parameterized SQL fragment with template strings and bound values.
3
+ * Used internally by the schema builder and where helpers.
4
+ */
1
5
  export declare class SQL {
6
+ /** Template string parts (interleaved with values). */
2
7
  strings: TemplateStringsArray;
8
+ /** Bound parameter values. */
3
9
  values: unknown[];
4
10
  constructor(strings: TemplateStringsArray, values: unknown[]);
11
+ /** Serialize to a raw SQL string (interpolating values inline for DDL use). */
5
12
  toSQL(): string;
6
13
  }
14
+ /**
15
+ * Tagged template helper for creating parameterized SQL fragments.
16
+ *
17
+ * ```ts
18
+ * sql`NOW()`
19
+ * sql`${column} ILIKE ${'%' + search + '%'}`
20
+ * ```
21
+ */
7
22
  export declare function sql(strings: TemplateStringsArray, ...values: unknown[]): SQL;
@@ -1,30 +1,69 @@
1
1
  import type { Sql } from '../../vendor.ts';
2
2
  import { ColumnBuilder, type PartitionByDef } from './columns.ts';
3
3
  import { SQL } from './sql.ts';
4
+ /** Options for table index creation. */
4
5
  export interface IndexOptions {
6
+ /** Whether the index should be UNIQUE. */
5
7
  unique?: boolean;
8
+ /** Index type: btree (default), hnsw (pgvector), gin (JSONB). */
6
9
  type?: 'btree' | 'hnsw' | 'gin';
10
+ /** Create index in DESC order. */
7
11
  desc?: boolean;
12
+ /** Custom operator class (e.g. `vector_cosine_ops`). */
8
13
  operator?: string;
9
14
  }
15
+ /** Options for CREATE TABLE. */
10
16
  export interface CreateOptions {
17
+ /** Partition by clause (RANGE, LIST, or HASH). */
11
18
  partitionBy?: PartitionByDef;
12
19
  }
20
+ /** Options for find/read queries. */
13
21
  export interface FindOptions {
22
+ /** ORDER BY clause: `{ column: 'asc' | 'desc' }`. */
14
23
  orderBy?: Record<string, 'asc' | 'desc'>;
24
+ /** LIMIT. */
15
25
  limit?: number;
26
+ /** OFFSET. */
16
27
  offset?: number;
28
+ /** Columns to SELECT (default: all). */
17
29
  select?: string[];
30
+ /** Include soft-deleted rows (also sets `withDeleted` context). */
18
31
  withDeleted?: boolean;
19
32
  }
33
+ /**
34
+ * Type-safe table schema + CRUD operations.
35
+ *
36
+ * Create an instance with {@link pgTable}, then call `.bind(sql)` to get a
37
+ * `BoundTable` for running queries.
38
+ *
39
+ * ```ts
40
+ * const users = pgTable('users', {
41
+ * id: serial('id').primaryKey(),
42
+ * name: text('name').notNull(),
43
+ * email: text('email').unique(),
44
+ * })
45
+ *
46
+ * const db = users.bind(sql)
47
+ * await db.create()
48
+ * await db.insert({ name: 'Alice', email: 'a@b.com' })
49
+ * const row = await db.findBy({ email: 'a@b.com' })
50
+ * ```
51
+ */
20
52
  export declare class Table<R extends Record<string, unknown>> {
53
+ /** Database table name. */
21
54
  readonly tableName: string;
55
+ /** All column builders (order-preserving). */
22
56
  readonly columns: ColumnBuilder<unknown>[];
57
+ /** Column builders keyed by property name. */
23
58
  readonly builders: Record<string, ColumnBuilder<unknown>>;
24
59
  private colEntries;
25
60
  constructor(tableName: string, builders: Record<string, ColumnBuilder<unknown>>);
61
+ /** Check if the table has a column with the given DB name. */
26
62
  hasColumn(dbName: string): boolean;
27
- /** Returns a BoundTable pre-bound to the given sql connection (no need to pass sql to every method). */
63
+ /**
64
+ * Bind this table schema to a SQL connection, returning a `BoundTable`
65
+ * that can run queries without passing `sql` to every call.
66
+ */
28
67
  bind(sql: Sql<{}>): BoundTable<R>;
29
68
  /** Returns the primary key column name (DB name), or 'id' as fallback. */
30
69
  private get pkColumn();
@@ -81,6 +120,20 @@ export declare class BoundTable<R extends Record<string, unknown>> {
81
120
  count(where?: Partial<R> | SQL | SQL[]): Promise<number>;
82
121
  withSql(sql: Sql<{}>): BoundTable<R>;
83
122
  }
123
+ /**
124
+ * Define a type-safe table schema.
125
+ *
126
+ * ```ts
127
+ * const users = pgTable('users', {
128
+ * id: serial('id').primaryKey(),
129
+ * name: text('name').notNull(),
130
+ * email: text('email').unique(),
131
+ * })
132
+ *
133
+ * // The generic type R preserves the column types:
134
+ * // Table<{ id: number; name: string; email: string }>
135
+ * ```
136
+ */
84
137
  export declare function pgTable<R extends Record<string, unknown>>(tableName: string, builders: {
85
138
  [K in keyof R]: ColumnBuilder<R[K]>;
86
139
  }): Table<R>;