@palbase/backend 24.2.0 → 25.0.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 (61) hide show
  1. package/dist/bin/palbase-backend.cjs +101 -60
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +17 -13
  4. package/dist/bin/palbase-backend.js.map +1 -1
  5. package/dist/{chunk-EIXCY4SS.js → chunk-43A3KGWL.js} +80 -49
  6. package/dist/chunk-43A3KGWL.js.map +1 -0
  7. package/dist/{chunk-ERDL5VAE.js → chunk-5CMLOAEF.js} +2 -2
  8. package/dist/chunk-OEQBHE2Z.js +825 -0
  9. package/dist/chunk-OEQBHE2Z.js.map +1 -0
  10. package/dist/{chunk-7Z6MGMXQ.js → chunk-XJ2RSHEU.js} +11 -5
  11. package/dist/chunk-XJ2RSHEU.js.map +1 -0
  12. package/dist/{chunk-UWSYTUGM.js → chunk-ZQRWW37O.js} +44 -1
  13. package/dist/chunk-ZQRWW37O.js.map +1 -0
  14. package/dist/db/env.cjs.map +1 -1
  15. package/dist/db/env.d.cts +29 -13
  16. package/dist/db/env.d.ts +29 -13
  17. package/dist/db/index.cjs +233 -110
  18. package/dist/db/index.cjs.map +1 -1
  19. package/dist/db/index.d.cts +1 -1
  20. package/dist/db/index.d.ts +1 -1
  21. package/dist/db/index.js +11 -1
  22. package/dist/engine/index.cjs +87 -50
  23. package/dist/engine/index.cjs.map +1 -1
  24. package/dist/engine/index.d.cts +2 -2
  25. package/dist/engine/index.d.ts +2 -2
  26. package/dist/engine/index.js +3 -3
  27. package/dist/{index-C0PMn5jl.d.ts → index-BF1f0DfA.d.ts} +5 -2
  28. package/dist/{index-DAwHMppB.d.cts → index-CoaDN9dL.d.cts} +5 -2
  29. package/dist/{index-ByBMibIJ.d.ts → index-Ct1iiB4N.d.ts} +232 -60
  30. package/dist/{index-D4rts8T7.d.cts → index-CwaWRhyc.d.cts} +232 -60
  31. package/dist/index.cjs +572 -296
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +124 -20
  34. package/dist/index.d.ts +124 -20
  35. package/dist/index.js +164 -216
  36. package/dist/index.js.map +1 -1
  37. package/dist/openapi/index.cjs +100 -36
  38. package/dist/openapi/index.cjs.map +1 -1
  39. package/dist/openapi/index.js +59 -2
  40. package/dist/openapi/index.js.map +1 -1
  41. package/docs/README.md +64 -31
  42. package/docs/endpoints.md +25 -28
  43. package/docs/llms-full.txt +465 -148
  44. package/docs/schema.md +338 -86
  45. package/docs/services.md +39 -4
  46. package/package.json +1 -1
  47. package/template/AGENTS.md +119 -314
  48. package/template/CLAUDE.md +13 -0
  49. package/template/controllers/notes.controller.ts +6 -13
  50. package/template/db/public.ts +38 -0
  51. package/template/models/notes/create.ts +38 -0
  52. package/template/package.json +6 -3
  53. package/template/services/note.service.test.ts +45 -0
  54. package/template/services/note.service.ts +2 -2
  55. package/dist/chunk-7Z6MGMXQ.js.map +0 -1
  56. package/dist/chunk-EIXCY4SS.js.map +0 -1
  57. package/dist/chunk-LCL7TUAI.js +0 -534
  58. package/dist/chunk-LCL7TUAI.js.map +0 -1
  59. package/dist/chunk-UWSYTUGM.js.map +0 -1
  60. package/template/db/schema.ts +0 -35
  61. /package/dist/{chunk-ERDL5VAE.js.map → chunk-5CMLOAEF.js.map} +0 -0
package/dist/index.js CHANGED
@@ -2,27 +2,33 @@ import {
2
2
  EXTENSION_DEPENDENCIES,
3
3
  PALBASE_EXTENSIONS,
4
4
  PolicyBuilder,
5
+ TABLE_META,
5
6
  bigint,
6
7
  boolean,
7
8
  defineSchema,
9
+ defineTable,
8
10
  enumType,
11
+ installationRef,
9
12
  integer,
10
13
  isPalbaseExtension,
11
14
  jsonb,
12
15
  makeTypedDB,
13
16
  numeric,
14
17
  openai,
18
+ ownedByUser,
15
19
  policy,
16
20
  raw,
17
21
  text,
18
22
  timestamp,
23
+ toSchemaJSON,
24
+ userRef,
19
25
  uuid,
20
26
  vector
21
- } from "./chunk-LCL7TUAI.js";
27
+ } from "./chunk-OEQBHE2Z.js";
22
28
  import {
23
29
  defineError,
24
30
  getErrorRegistry
25
- } from "./chunk-ERDL5VAE.js";
31
+ } from "./chunk-5CMLOAEF.js";
26
32
  import {
27
33
  Cache,
28
34
  Database,
@@ -41,7 +47,7 @@ import {
41
47
  __setRuntime,
42
48
  onShutdown,
43
49
  onStart
44
- } from "./chunk-7Z6MGMXQ.js";
50
+ } from "./chunk-XJ2RSHEU.js";
45
51
  import {
46
52
  TxPlanError,
47
53
  TxRefError,
@@ -67,10 +73,13 @@ import {
67
73
  getRegisteredControllers,
68
74
  getRoutes,
69
75
  recordParam,
76
+ recordRoom,
77
+ recordRoomHook,
78
+ recordRoomMessage,
70
79
  recordRoute,
71
80
  recordThrows,
72
81
  resolveEffectiveAuth
73
- } from "./chunk-UWSYTUGM.js";
82
+ } from "./chunk-ZQRWW37O.js";
74
83
  import "./chunk-7D4SUZUM.js";
75
84
 
76
85
  // src/clients/http.ts
@@ -948,167 +957,6 @@ function buildModuleClients(cfg) {
948
957
  };
949
958
  }
950
959
 
951
- // src/db/schema-json.ts
952
- function defOf(column) {
953
- return "_def" in column ? column._def : column;
954
- }
955
- function columnToJSON(column) {
956
- const def = defOf(column);
957
- const out = {
958
- type: def.type,
959
- nullable: def.nullable,
960
- primaryKey: def.primaryKey
961
- };
962
- if (def.defaultValue !== void 0) out.defaultValue = def.defaultValue;
963
- if (def.defaultRandom === true) out.defaultRandom = true;
964
- if (def.defaultNow === true) out.defaultNow = true;
965
- if (def.renamedFrom !== void 0) out.renamedFrom = def.renamedFrom;
966
- if (def.references !== void 0) {
967
- out.references = { table: def.references.table, column: def.references.column };
968
- }
969
- if (def.onDeleteAction !== void 0) out.onDeleteAction = def.onDeleteAction;
970
- if (def.enumName !== void 0) out.enumName = def.enumName;
971
- if (def.enumValues !== void 0) out.enumValues = [...def.enumValues];
972
- if (def.unique === true) out.unique = true;
973
- if (def.dimensions !== void 0) out.dimensions = def.dimensions;
974
- return out;
975
- }
976
- function policyToJSON(policy2) {
977
- return {
978
- name: policy2.name,
979
- command: policy2.command ?? "all",
980
- roles: policy2.roles ? [...policy2.roles] : [],
981
- // null rather than omitted: a policy with no USING clause is a different
982
- // thing from one whose clause the emitter forgot, and Go reads the
983
- // difference.
984
- using: policy2.using ?? null,
985
- withCheck: policy2.withCheck ?? null,
986
- permissive: policy2.permissive !== false
987
- };
988
- }
989
- function commonSearchFields(search, out) {
990
- if (search.synonyms !== void 0 && Object.keys(search.synonyms).length > 0) {
991
- out.synonyms = Object.fromEntries(
992
- Object.entries(search.synonyms).map(([word, alts]) => [word, [...alts]])
993
- );
994
- }
995
- if (search.validity === true) out.validity = true;
996
- }
997
- function searchToJSON(search, vectorColumn) {
998
- if (search.from !== void 0 && search.model !== void 0) {
999
- const out2 = {};
1000
- const textCols = search.text === false ? void 0 : Array.isArray(search.text) && search.text.length > 0 ? search.text : search.from;
1001
- if (textCols !== void 0) out2.text = { columns: [...textCols] };
1002
- const v = { metric: search.metric ?? "cosine" };
1003
- if (vectorColumn !== void 0) v.column = vectorColumn;
1004
- v.embed = {
1005
- provider: search.model.provider,
1006
- model: search.model.model,
1007
- from: [...search.from],
1008
- ...search.model.apiKeyName !== void 0 ? { apiKeyName: search.model.apiKeyName } : {},
1009
- ...search.model.dimensions !== void 0 ? { dimensions: search.model.dimensions } : {},
1010
- ...search.model.baseURL !== void 0 ? { baseURL: search.model.baseURL } : {}
1011
- };
1012
- if (search.staleness !== void 0) v.staleness = search.staleness;
1013
- if (vectorColumn === void 0) {
1014
- v.mode = "chunks";
1015
- if (search.chunks !== void 0) {
1016
- const c = {};
1017
- if (search.chunks.size !== void 0 && search.chunks.size > 0) c.sizeChars = search.chunks.size;
1018
- if (search.chunks.overlap !== void 0 && search.chunks.overlap > 0) c.overlapChars = search.chunks.overlap;
1019
- if (Object.keys(c).length > 0) v.chunks = c;
1020
- }
1021
- }
1022
- out2.vector = [v];
1023
- commonSearchFields(search, out2);
1024
- return out2;
1025
- }
1026
- const out = {};
1027
- if (Array.isArray(search.text) && search.text.length > 0) out.text = { columns: [...search.text] };
1028
- const legs = search.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
1029
- if (legs.length > 0) {
1030
- out.vector = legs.map((leg) => {
1031
- const v = { metric: leg.metric ?? "cosine" };
1032
- if (leg.column !== void 0) v.column = leg.column;
1033
- if (leg.staleness !== void 0) v.staleness = leg.staleness;
1034
- if (leg.model !== void 0) {
1035
- v.embed = {
1036
- provider: leg.model.provider,
1037
- model: leg.model.model,
1038
- from: [...leg.from ?? []],
1039
- ...leg.model.apiKeyName !== void 0 ? { apiKeyName: leg.model.apiKeyName } : {},
1040
- ...leg.model.dimensions !== void 0 ? { dimensions: leg.model.dimensions } : {},
1041
- ...leg.model.baseURL !== void 0 ? { baseURL: leg.model.baseURL } : {}
1042
- };
1043
- }
1044
- return v;
1045
- });
1046
- }
1047
- commonSearchFields(search, out);
1048
- return out;
1049
- }
1050
- function memoryToJSON(m) {
1051
- return {
1052
- from: [...m.from],
1053
- into: m.into,
1054
- ...m.subject !== void 0 ? { subject: m.subject } : {},
1055
- extract: { provider: m.extract.provider, model: m.extract.model }
1056
- };
1057
- }
1058
- function tableToJSON(table) {
1059
- const columns = {};
1060
- for (const [name, column] of Object.entries(table.columns)) {
1061
- columns[name] = columnToJSON(column);
1062
- }
1063
- const out = {
1064
- name: table.name,
1065
- columns,
1066
- // Read, not re-derived. `defineSchema` already resolves the fail-closed
1067
- // default (RLS on unless the author wrote `rls: false`, and forced on by any
1068
- // policy), and a second copy of a SECURITY default is exactly the thing that
1069
- // drifts — the direction it drifted last time was "expose everything", and
1070
- // the live proof was one user reading another's rows.
1071
- rls: table.rls,
1072
- policies: (table.policies ?? []).map(policyToJSON)
1073
- };
1074
- if (table.primaryKey !== void 0 && table.primaryKey.length > 0) {
1075
- out.primaryKey = [...table.primaryKey];
1076
- }
1077
- if (table.unique !== void 0 && table.unique.length > 0) {
1078
- out.uniqueConstraints = table.unique.map((u) => ({ name: u.name, columns: [...u.columns] }));
1079
- }
1080
- if (table.raw !== void 0 && table.raw.length > 0) {
1081
- out.rawConstraints = table.raw.map((r) => ({
1082
- name: r.name,
1083
- up: r.up,
1084
- down: r.down ?? null
1085
- }));
1086
- }
1087
- if (table.checks !== void 0 && table.checks.length > 0) {
1088
- out.checks = table.checks.map((c) => ({ name: c.name, expr: c.expr }));
1089
- }
1090
- if (table.indexes !== void 0 && table.indexes.length > 0) {
1091
- out.indexes = table.indexes.map((i) => ({ name: i.name, columns: [...i.columns] }));
1092
- }
1093
- if (table.search !== void 0) {
1094
- const vectorColumn = Object.entries(columns).find(([, c]) => c.dimensions !== void 0)?.[0];
1095
- const sj = searchToJSON(table.search, vectorColumn);
1096
- if (sj.text !== void 0 || sj.vector !== void 0) out.search = sj;
1097
- }
1098
- if (table.memory !== void 0) {
1099
- out.memory = memoryToJSON(table.memory);
1100
- }
1101
- return out;
1102
- }
1103
- function toSchemaJSON(schema) {
1104
- const tables = {};
1105
- for (const table of Object.values(schema.tables)) {
1106
- const json = tableToJSON(table);
1107
- tables[json.name] = json;
1108
- }
1109
- return { tables, extensions: [...new Set(schema.extensions ?? [])] };
1110
- }
1111
-
1112
960
  // src/channels.ts
1113
961
  var CHANNELS = /* @__PURE__ */ Symbol.for("palbase.backend.channels");
1114
962
  function ownerOnly() {
@@ -1208,50 +1056,73 @@ function rowType(def) {
1208
1056
  function optionalOnInsert(def) {
1209
1057
  return def.nullable === true || def.defaultRandom === true || def.defaultNow === true || def.defaultValue !== void 0;
1210
1058
  }
1211
- var AUTH_USERS_TABLE = "auth.users";
1212
- function ownerColumnOf(table) {
1213
- for (const [col, builder] of Object.entries(table.columns)) {
1214
- if (builder._def.references?.table === AUTH_USERS_TABLE) return col;
1215
- }
1216
- return null;
1059
+ function forwardName(column) {
1060
+ return column.endsWith("_id") ? column.slice(0, -3) : column;
1217
1061
  }
1218
- function buildChildEdges(schema) {
1219
- const edges = /* @__PURE__ */ new Map();
1220
- for (const name of Object.keys(schema.tables)) edges.set(name, []);
1221
- for (const childName of Object.keys(schema.tables)) {
1222
- const table = schema.tables[childName];
1223
- if (table === void 0) continue;
1224
- for (const [col, builder] of Object.entries(table.columns)) {
1225
- const ref = builder._def.references;
1226
- if (ref === void 0) continue;
1227
- if (ref.table === childName) continue;
1228
- const bucket = edges.get(ref.table);
1229
- if (bucket === void 0) continue;
1230
- if (bucket.some((e) => e.child === childName)) continue;
1231
- bucket.push({ child: childName, column: col });
1062
+ function qualify(schemaName, tableName) {
1063
+ return schemaName === "public" ? tableName : `${schemaName}.${tableName}`;
1064
+ }
1065
+ function buildRelations(schemas) {
1066
+ const out = /* @__PURE__ */ new Map();
1067
+ const keyOf = /* @__PURE__ */ new Map();
1068
+ for (const schema of schemas) {
1069
+ for (const t of Object.values(schema.tables)) {
1070
+ const key = qualify(schema.name, t.name);
1071
+ out.set(key, []);
1072
+ keyOf.set(t.name, key);
1232
1073
  }
1233
1074
  }
1234
- return edges;
1235
- }
1236
- function pruneCycles(raw2) {
1237
- const pruned = /* @__PURE__ */ new Map();
1238
- for (const table of raw2.keys()) pruned.set(table, []);
1239
- const state = /* @__PURE__ */ new Map();
1240
- function visit(table) {
1241
- state.set(table, "visiting");
1242
- for (const edge of raw2.get(table) ?? []) {
1243
- if (state.get(edge.child) === "visiting") continue;
1244
- pruned.get(table)?.push(edge);
1245
- if (state.get(edge.child) === void 0) visit(edge.child);
1075
+ for (const schema of schemas) {
1076
+ for (const table of Object.values(schema.tables)) {
1077
+ const childKey = qualify(schema.name, table.name);
1078
+ const bucket = out.get(childKey);
1079
+ if (bucket === void 0) continue;
1080
+ const byTarget = /* @__PURE__ */ new Map();
1081
+ for (const [col, builder] of Object.entries(table.columns)) {
1082
+ const ref = builder._def.references;
1083
+ if (ref === void 0) continue;
1084
+ const list = byTarget.get(ref.table) ?? [];
1085
+ list.push(col);
1086
+ byTarget.set(ref.table, list);
1087
+ }
1088
+ for (const [target, cols] of byTarget) {
1089
+ if (cols.length < 2) continue;
1090
+ const unnamed = cols.filter((c) => {
1091
+ const d = table.columns[c]?._def;
1092
+ return d?.refAs === void 0 && d?.owns !== true;
1093
+ });
1094
+ if (unnamed.length > 0) {
1095
+ throw new Error(
1096
+ `table "${table.name}" declares ${cols.length} foreign keys onto "${target}" (${cols.join(", ")}) \u2014 name each one: references(() => \u2026, { as: "\u2026" })`
1097
+ );
1098
+ }
1099
+ }
1100
+ for (const [col, builder] of Object.entries(table.columns)) {
1101
+ const def = builder._def;
1102
+ if (def.references === void 0) continue;
1103
+ const name = def.owns === true ? "owner" : def.refAs ?? forwardName(col);
1104
+ if (bucket.some((r) => r.name === name)) {
1105
+ throw new Error(
1106
+ name === "owner" ? `table "${table.name}": two columns claim ownership of the row \u2014 a table has at most ONE ownedByUser(). If the other column only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead.` : `table "${table.name}": two relations resolve to the name "${name}" \u2014 give one an explicit { as: "\u2026" }`
1107
+ );
1108
+ }
1109
+ const targetKey = keyOf.get(def.references.table) ?? def.references.table;
1110
+ bucket.push({ name, to: targetKey, kind: "one", via: col });
1111
+ const parent = out.get(targetKey);
1112
+ if (parent === void 0 || targetKey === childKey) continue;
1113
+ const reverse = def.refAs ?? table.name;
1114
+ if (parent.some((r) => r.name === reverse)) {
1115
+ throw new Error(
1116
+ `table "${def.references.table}": two reverse relations resolve to the name "${reverse}" \u2014 give one an explicit { as: "\u2026" }`
1117
+ );
1118
+ }
1119
+ parent.push({ name: reverse, to: childKey, kind: "many", via: col });
1120
+ }
1246
1121
  }
1247
- state.set(table, "done");
1248
1122
  }
1249
- for (const table of raw2.keys()) {
1250
- if (state.get(table) === void 0) visit(table);
1251
- }
1252
- return pruned;
1123
+ return out;
1253
1124
  }
1254
- function tableBlock(table, children, indent) {
1125
+ function tableBlock(table, relations, indent) {
1255
1126
  const cols = Object.entries(table.columns);
1256
1127
  const rowLines = cols.map(([col, builder]) => {
1257
1128
  return `${indent} ${col}: ${rowType(builder._def)};`;
@@ -1261,8 +1132,9 @@ function tableBlock(table, children, indent) {
1261
1132
  const opt = optionalOnInsert(def) ? "?" : "";
1262
1133
  return `${indent} ${col}${opt}: ${rowType(def)};`;
1263
1134
  });
1264
- const owner = ownerColumnOf(table);
1265
- const childEntries = children.map((e) => `${JSON.stringify(e.child)}: ${JSON.stringify(e.column)}`).join("; ");
1135
+ const relEntries = relations.map(
1136
+ (r) => `${r.name}: { to: ${JSON.stringify(r.to)}; kind: ${JSON.stringify(r.kind)}; via: ${JSON.stringify(r.via)} }`
1137
+ ).join("; ");
1266
1138
  return [
1267
1139
  `${indent}${table.name}: {`,
1268
1140
  `${indent} row: {`,
@@ -1271,8 +1143,7 @@ function tableBlock(table, children, indent) {
1271
1143
  `${indent} insert: {`,
1272
1144
  ...insertLines,
1273
1145
  `${indent} };`,
1274
- `${indent} owner: ${owner === null ? "null" : JSON.stringify(owner)};`,
1275
- `${indent} children: {${childEntries === "" ? "" : ` ${childEntries} `}};`,
1146
+ `${indent} relations: {${relEntries === "" ? "" : ` ${relEntries} `}};`,
1276
1147
  // searchable: vector kolonu YA DA search beyanı → EnvTypedTable'da search()
1277
1148
  // üyesini açan yapısal bayrak (FR-013). Yokken satır hiç üretilmez ki
1278
1149
  // mevcut şemaların d.ts'i bayt-aynı kalsın.
@@ -1280,22 +1151,37 @@ function tableBlock(table, children, indent) {
1280
1151
  `${indent}};`
1281
1152
  ].join("\n");
1282
1153
  }
1283
- function makeEnvDts(schema) {
1284
- const tableNames = Object.keys(schema.tables);
1285
- const children = pruneCycles(buildChildEdges(schema));
1286
- const blocks = tableNames.map(
1287
- (name) => tableBlock(schema.tables[name], children.get(name) ?? [], " ")
1154
+ function makeEnvDts(schemas) {
1155
+ const relations = buildRelations(schemas);
1156
+ const qualify2 = (schemaName, table) => schemaName === "public" ? table : `${schemaName}.${table}`;
1157
+ const publicSchema = schemas.find((s) => s.name === "public");
1158
+ const others = schemas.filter((s) => s.name !== "public");
1159
+ const publicBlocks = Object.keys(publicSchema?.tables ?? {}).map(
1160
+ (name) => tableBlock(publicSchema.tables[name], relations.get(name) ?? [], " ")
1288
1161
  );
1289
- const body = blocks.length > 0 ? `
1290
- ${blocks.join("\n")}
1162
+ const body = publicBlocks.length > 0 ? `
1163
+ ${publicBlocks.join("\n")}
1164
+ ` : "";
1165
+ const schemaBlocks = others.map((schema) => {
1166
+ const inner = Object.keys(schema.tables).map(
1167
+ (name) => tableBlock(schema.tables[name], relations.get(qualify2(schema.name, name)) ?? [], " ")
1168
+ );
1169
+ return ` ${schema.name}: {
1170
+ ${inner.join("\n")}
1171
+ };`;
1172
+ });
1173
+ const schemasBody = schemaBlocks.length > 0 ? `
1174
+ ${schemaBlocks.join("\n")}
1291
1175
  ` : "";
1292
1176
  return `// AUTO-GENERATED by @palbase/backend \u2014 DO NOT EDIT.
1293
- // Regenerated from db/schema.ts by \`palbase build\` and by every deploy.
1177
+ // Regenerated from db/*.ts by \`palbase build\` and by every deploy.
1294
1178
  // Augments the @palbase/backend/env \`Tables\` interface so \`Database.tables.*\`
1295
- // is typed with no import and no generic.
1179
+ // is typed with no import and no generic. Schemas other than \`public\` land
1180
+ // under \`Schemas\`, reached with \`Database.schema("<name>").tables.*\`.
1296
1181
 
1297
1182
  declare module "@palbase/backend/env" {
1298
1183
  interface Tables {${body}}
1184
+ interface Schemas {${schemasBody}}
1299
1185
  }
1300
1186
 
1301
1187
  export {};
@@ -1576,6 +1462,56 @@ function Signal() {
1576
1462
  };
1577
1463
  }
1578
1464
 
1465
+ // src/decorators/room.ts
1466
+ var DEFAULT_GRACE_MS = 6e4;
1467
+ function assertPattern(pattern) {
1468
+ if (pattern.includes("/")) {
1469
+ throw new Error(
1470
+ `@Room("${pattern}"): channel patterns are colon-separated, not slash-separated. Write "${pattern.replace(/\//g, ":")}" \u2014 a slash matches no live topic.`
1471
+ );
1472
+ }
1473
+ const segments = pattern.split(":");
1474
+ if (segments.some((s) => s === "")) {
1475
+ throw new Error(`@Room("${pattern}"): empty segment \u2014 every segment must be non-empty.`);
1476
+ }
1477
+ const seen = /* @__PURE__ */ new Set();
1478
+ for (const s of segments) {
1479
+ if (!s.startsWith("{") || !s.endsWith("}")) continue;
1480
+ const name = s.slice(1, -1);
1481
+ if (name === "") throw new Error(`@Room("${pattern}"): "{}" names no parameter.`);
1482
+ if (seen.has(name)) {
1483
+ throw new Error(`@Room("${pattern}"): parameter "{${name}}" appears twice.`);
1484
+ }
1485
+ seen.add(name);
1486
+ }
1487
+ }
1488
+ function Room(pattern, options) {
1489
+ return function(ctor) {
1490
+ assertPattern(pattern);
1491
+ recordRoom(ctor, {
1492
+ pattern,
1493
+ events: options.events,
1494
+ graceMs: options.graceMs ?? DEFAULT_GRACE_MS
1495
+ });
1496
+ return ctor;
1497
+ };
1498
+ }
1499
+ function hookDecorator(hook) {
1500
+ return () => function(target, propertyKey) {
1501
+ recordRoomHook(target, hook, String(propertyKey));
1502
+ };
1503
+ }
1504
+ var OnAuthorize = hookDecorator("authorize");
1505
+ var OnFirst = hookDecorator("first");
1506
+ var OnJoin = hookDecorator("join");
1507
+ var OnLeave = hookDecorator("leave");
1508
+ var OnEmpty = hookDecorator("empty");
1509
+ function OnMessage(name, schema) {
1510
+ return function(target, propertyKey) {
1511
+ recordRoomMessage(target, name, String(propertyKey), schema);
1512
+ };
1513
+ }
1514
+
1579
1515
  // src/decorators/params.ts
1580
1516
  function makeParamDecorator(kind, extra) {
1581
1517
  return function(target, propertyKey, parameterIndex) {
@@ -1776,6 +1712,12 @@ export {
1776
1712
  NotFound,
1777
1713
  Notifications,
1778
1714
  On,
1715
+ OnAuthorize,
1716
+ OnEmpty,
1717
+ OnFirst,
1718
+ OnJoin,
1719
+ OnLeave,
1720
+ OnMessage,
1779
1721
  OptionalUser,
1780
1722
  PALBASE_EXTENSIONS,
1781
1723
  PalError,
@@ -1790,11 +1732,13 @@ export {
1790
1732
  Realtime,
1791
1733
  Req,
1792
1734
  RequestId,
1735
+ Room,
1793
1736
  Secrets,
1794
1737
  Signal,
1795
1738
  Sse,
1796
1739
  SseOut,
1797
1740
  Storage,
1741
+ TABLE_META,
1798
1742
  TooManyRequests,
1799
1743
  TraceId,
1800
1744
  TxPlanError,
@@ -1823,6 +1767,7 @@ export {
1823
1767
  defineError,
1824
1768
  defineMiddleware,
1825
1769
  defineSchema,
1770
+ defineTable,
1826
1771
  enumType,
1827
1772
  getErrorRegistry,
1828
1773
  getHookConfig,
@@ -1831,6 +1776,7 @@ export {
1831
1776
  getRoutes,
1832
1777
  getWebhookConfig,
1833
1778
  inc,
1779
+ installationRef,
1834
1780
  integer,
1835
1781
  isPalbaseExtension,
1836
1782
  jsonb,
@@ -1843,6 +1789,7 @@ export {
1843
1789
  onShutdown,
1844
1790
  onStart,
1845
1791
  openai,
1792
+ ownedByUser,
1846
1793
  ownerOnly,
1847
1794
  policy,
1848
1795
  publicChannel,
@@ -1852,6 +1799,7 @@ export {
1852
1799
  text,
1853
1800
  timestamp,
1854
1801
  toSchemaJSON,
1802
+ userRef,
1855
1803
  uuid,
1856
1804
  vector,
1857
1805
  z