@palbase/backend 24.3.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 +212 -111
  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-DEneI8Mn.d.ts → index-BF1f0DfA.d.ts} +5 -2
  28. package/dist/{index-C-ALG22n.d.cts → index-CoaDN9dL.d.cts} +5 -2
  29. package/dist/{index-BTMYod_l.d.ts → index-Ct1iiB4N.d.ts} +203 -61
  30. package/dist/{index-BLAbr9ZH.d.cts → index-CwaWRhyc.d.cts} +203 -61
  31. package/dist/index.cjs +550 -297
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +122 -20
  34. package/dist/index.d.ts +122 -20
  35. package/dist/index.js +164 -217
  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 +399 -153
  44. package/docs/schema.md +272 -91
  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-D5CQES25.js +0 -556
  57. package/dist/chunk-D5CQES25.js.map +0 -1
  58. package/dist/chunk-EIXCY4SS.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-D5CQES25.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,168 +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.ignored === true) out.ignored = true;
967
- if (def.references !== void 0) {
968
- out.references = { table: def.references.table, column: def.references.column };
969
- }
970
- if (def.onDeleteAction !== void 0) out.onDeleteAction = def.onDeleteAction;
971
- if (def.enumName !== void 0) out.enumName = def.enumName;
972
- if (def.enumValues !== void 0) out.enumValues = [...def.enumValues];
973
- if (def.unique === true) out.unique = true;
974
- if (def.dimensions !== void 0) out.dimensions = def.dimensions;
975
- return out;
976
- }
977
- function policyToJSON(policy2) {
978
- return {
979
- name: policy2.name,
980
- command: policy2.command ?? "all",
981
- roles: policy2.roles ? [...policy2.roles] : [],
982
- // null rather than omitted: a policy with no USING clause is a different
983
- // thing from one whose clause the emitter forgot, and Go reads the
984
- // difference.
985
- using: policy2.using ?? null,
986
- withCheck: policy2.withCheck ?? null,
987
- permissive: policy2.permissive !== false
988
- };
989
- }
990
- function commonSearchFields(search, out) {
991
- if (search.synonyms !== void 0 && Object.keys(search.synonyms).length > 0) {
992
- out.synonyms = Object.fromEntries(
993
- Object.entries(search.synonyms).map(([word, alts]) => [word, [...alts]])
994
- );
995
- }
996
- if (search.validity === true) out.validity = true;
997
- }
998
- function searchToJSON(search, vectorColumn) {
999
- if (search.from !== void 0 && search.model !== void 0) {
1000
- const out2 = {};
1001
- const textCols = search.text === false ? void 0 : Array.isArray(search.text) && search.text.length > 0 ? search.text : search.from;
1002
- if (textCols !== void 0) out2.text = { columns: [...textCols] };
1003
- const v = { metric: search.metric ?? "cosine" };
1004
- if (vectorColumn !== void 0) v.column = vectorColumn;
1005
- v.embed = {
1006
- provider: search.model.provider,
1007
- model: search.model.model,
1008
- from: [...search.from],
1009
- ...search.model.apiKeyName !== void 0 ? { apiKeyName: search.model.apiKeyName } : {},
1010
- ...search.model.dimensions !== void 0 ? { dimensions: search.model.dimensions } : {},
1011
- ...search.model.baseURL !== void 0 ? { baseURL: search.model.baseURL } : {}
1012
- };
1013
- if (search.staleness !== void 0) v.staleness = search.staleness;
1014
- if (vectorColumn === void 0) {
1015
- v.mode = "chunks";
1016
- if (search.chunks !== void 0) {
1017
- const c = {};
1018
- if (search.chunks.size !== void 0 && search.chunks.size > 0) c.sizeChars = search.chunks.size;
1019
- if (search.chunks.overlap !== void 0 && search.chunks.overlap > 0) c.overlapChars = search.chunks.overlap;
1020
- if (Object.keys(c).length > 0) v.chunks = c;
1021
- }
1022
- }
1023
- out2.vector = [v];
1024
- commonSearchFields(search, out2);
1025
- return out2;
1026
- }
1027
- const out = {};
1028
- if (Array.isArray(search.text) && search.text.length > 0) out.text = { columns: [...search.text] };
1029
- const legs = search.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
1030
- if (legs.length > 0) {
1031
- out.vector = legs.map((leg) => {
1032
- const v = { metric: leg.metric ?? "cosine" };
1033
- if (leg.column !== void 0) v.column = leg.column;
1034
- if (leg.staleness !== void 0) v.staleness = leg.staleness;
1035
- if (leg.model !== void 0) {
1036
- v.embed = {
1037
- provider: leg.model.provider,
1038
- model: leg.model.model,
1039
- from: [...leg.from ?? []],
1040
- ...leg.model.apiKeyName !== void 0 ? { apiKeyName: leg.model.apiKeyName } : {},
1041
- ...leg.model.dimensions !== void 0 ? { dimensions: leg.model.dimensions } : {},
1042
- ...leg.model.baseURL !== void 0 ? { baseURL: leg.model.baseURL } : {}
1043
- };
1044
- }
1045
- return v;
1046
- });
1047
- }
1048
- commonSearchFields(search, out);
1049
- return out;
1050
- }
1051
- function memoryToJSON(m) {
1052
- return {
1053
- from: [...m.from],
1054
- into: m.into,
1055
- ...m.subject !== void 0 ? { subject: m.subject } : {},
1056
- extract: { provider: m.extract.provider, model: m.extract.model }
1057
- };
1058
- }
1059
- function tableToJSON(table) {
1060
- const columns = {};
1061
- for (const [name, column] of Object.entries(table.columns)) {
1062
- columns[name] = columnToJSON(column);
1063
- }
1064
- const out = {
1065
- name: table.name,
1066
- columns,
1067
- // Read, not re-derived. `defineSchema` already resolves the fail-closed
1068
- // default (RLS on unless the author wrote `rls: false`, and forced on by any
1069
- // policy), and a second copy of a SECURITY default is exactly the thing that
1070
- // drifts — the direction it drifted last time was "expose everything", and
1071
- // the live proof was one user reading another's rows.
1072
- rls: table.rls,
1073
- policies: (table.policies ?? []).map(policyToJSON)
1074
- };
1075
- if (table.primaryKey !== void 0 && table.primaryKey.length > 0) {
1076
- out.primaryKey = [...table.primaryKey];
1077
- }
1078
- if (table.unique !== void 0 && table.unique.length > 0) {
1079
- out.uniqueConstraints = table.unique.map((u) => ({ name: u.name, columns: [...u.columns] }));
1080
- }
1081
- if (table.raw !== void 0 && table.raw.length > 0) {
1082
- out.rawConstraints = table.raw.map((r) => ({
1083
- name: r.name,
1084
- up: r.up,
1085
- down: r.down ?? null
1086
- }));
1087
- }
1088
- if (table.checks !== void 0 && table.checks.length > 0) {
1089
- out.checks = table.checks.map((c) => ({ name: c.name, expr: c.expr }));
1090
- }
1091
- if (table.indexes !== void 0 && table.indexes.length > 0) {
1092
- out.indexes = table.indexes.map((i) => ({ name: i.name, columns: [...i.columns] }));
1093
- }
1094
- if (table.search !== void 0) {
1095
- const vectorColumn = Object.entries(columns).find(([, c]) => c.dimensions !== void 0)?.[0];
1096
- const sj = searchToJSON(table.search, vectorColumn);
1097
- if (sj.text !== void 0 || sj.vector !== void 0) out.search = sj;
1098
- }
1099
- if (table.memory !== void 0) {
1100
- out.memory = memoryToJSON(table.memory);
1101
- }
1102
- return out;
1103
- }
1104
- function toSchemaJSON(schema) {
1105
- const tables = {};
1106
- for (const table of Object.values(schema.tables)) {
1107
- const json = tableToJSON(table);
1108
- tables[json.name] = json;
1109
- }
1110
- return { tables, extensions: [...new Set(schema.extensions ?? [])] };
1111
- }
1112
-
1113
960
  // src/channels.ts
1114
961
  var CHANNELS = /* @__PURE__ */ Symbol.for("palbase.backend.channels");
1115
962
  function ownerOnly() {
@@ -1209,50 +1056,73 @@ function rowType(def) {
1209
1056
  function optionalOnInsert(def) {
1210
1057
  return def.nullable === true || def.defaultRandom === true || def.defaultNow === true || def.defaultValue !== void 0;
1211
1058
  }
1212
- var AUTH_USERS_TABLE = "auth.users";
1213
- function ownerColumnOf(table) {
1214
- for (const [col, builder] of Object.entries(table.columns)) {
1215
- if (builder._def.references?.table === AUTH_USERS_TABLE) return col;
1216
- }
1217
- return null;
1059
+ function forwardName(column) {
1060
+ return column.endsWith("_id") ? column.slice(0, -3) : column;
1218
1061
  }
1219
- function buildChildEdges(schema) {
1220
- const edges = /* @__PURE__ */ new Map();
1221
- for (const name of Object.keys(schema.tables)) edges.set(name, []);
1222
- for (const childName of Object.keys(schema.tables)) {
1223
- const table = schema.tables[childName];
1224
- if (table === void 0) continue;
1225
- for (const [col, builder] of Object.entries(table.columns)) {
1226
- const ref = builder._def.references;
1227
- if (ref === void 0) continue;
1228
- if (ref.table === childName) continue;
1229
- const bucket = edges.get(ref.table);
1230
- if (bucket === void 0) continue;
1231
- if (bucket.some((e) => e.child === childName)) continue;
1232
- 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);
1233
1073
  }
1234
1074
  }
1235
- return edges;
1236
- }
1237
- function pruneCycles(raw2) {
1238
- const pruned = /* @__PURE__ */ new Map();
1239
- for (const table of raw2.keys()) pruned.set(table, []);
1240
- const state = /* @__PURE__ */ new Map();
1241
- function visit(table) {
1242
- state.set(table, "visiting");
1243
- for (const edge of raw2.get(table) ?? []) {
1244
- if (state.get(edge.child) === "visiting") continue;
1245
- pruned.get(table)?.push(edge);
1246
- 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
+ }
1247
1121
  }
1248
- state.set(table, "done");
1249
1122
  }
1250
- for (const table of raw2.keys()) {
1251
- if (state.get(table) === void 0) visit(table);
1252
- }
1253
- return pruned;
1123
+ return out;
1254
1124
  }
1255
- function tableBlock(table, children, indent) {
1125
+ function tableBlock(table, relations, indent) {
1256
1126
  const cols = Object.entries(table.columns);
1257
1127
  const rowLines = cols.map(([col, builder]) => {
1258
1128
  return `${indent} ${col}: ${rowType(builder._def)};`;
@@ -1262,8 +1132,9 @@ function tableBlock(table, children, indent) {
1262
1132
  const opt = optionalOnInsert(def) ? "?" : "";
1263
1133
  return `${indent} ${col}${opt}: ${rowType(def)};`;
1264
1134
  });
1265
- const owner = ownerColumnOf(table);
1266
- 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("; ");
1267
1138
  return [
1268
1139
  `${indent}${table.name}: {`,
1269
1140
  `${indent} row: {`,
@@ -1272,8 +1143,7 @@ function tableBlock(table, children, indent) {
1272
1143
  `${indent} insert: {`,
1273
1144
  ...insertLines,
1274
1145
  `${indent} };`,
1275
- `${indent} owner: ${owner === null ? "null" : JSON.stringify(owner)};`,
1276
- `${indent} children: {${childEntries === "" ? "" : ` ${childEntries} `}};`,
1146
+ `${indent} relations: {${relEntries === "" ? "" : ` ${relEntries} `}};`,
1277
1147
  // searchable: vector kolonu YA DA search beyanı → EnvTypedTable'da search()
1278
1148
  // üyesini açan yapısal bayrak (FR-013). Yokken satır hiç üretilmez ki
1279
1149
  // mevcut şemaların d.ts'i bayt-aynı kalsın.
@@ -1281,22 +1151,37 @@ function tableBlock(table, children, indent) {
1281
1151
  `${indent}};`
1282
1152
  ].join("\n");
1283
1153
  }
1284
- function makeEnvDts(schema) {
1285
- const tableNames = Object.keys(schema.tables);
1286
- const children = pruneCycles(buildChildEdges(schema));
1287
- const blocks = tableNames.map(
1288
- (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) ?? [], " ")
1289
1161
  );
1290
- const body = blocks.length > 0 ? `
1291
- ${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")}
1292
1175
  ` : "";
1293
1176
  return `// AUTO-GENERATED by @palbase/backend \u2014 DO NOT EDIT.
1294
- // Regenerated from db/schema.ts by \`palbase build\` and by every deploy.
1177
+ // Regenerated from db/*.ts by \`palbase build\` and by every deploy.
1295
1178
  // Augments the @palbase/backend/env \`Tables\` interface so \`Database.tables.*\`
1296
- // 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.*\`.
1297
1181
 
1298
1182
  declare module "@palbase/backend/env" {
1299
1183
  interface Tables {${body}}
1184
+ interface Schemas {${schemasBody}}
1300
1185
  }
1301
1186
 
1302
1187
  export {};
@@ -1577,6 +1462,56 @@ function Signal() {
1577
1462
  };
1578
1463
  }
1579
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
+
1580
1515
  // src/decorators/params.ts
1581
1516
  function makeParamDecorator(kind, extra) {
1582
1517
  return function(target, propertyKey, parameterIndex) {
@@ -1777,6 +1712,12 @@ export {
1777
1712
  NotFound,
1778
1713
  Notifications,
1779
1714
  On,
1715
+ OnAuthorize,
1716
+ OnEmpty,
1717
+ OnFirst,
1718
+ OnJoin,
1719
+ OnLeave,
1720
+ OnMessage,
1780
1721
  OptionalUser,
1781
1722
  PALBASE_EXTENSIONS,
1782
1723
  PalError,
@@ -1791,11 +1732,13 @@ export {
1791
1732
  Realtime,
1792
1733
  Req,
1793
1734
  RequestId,
1735
+ Room,
1794
1736
  Secrets,
1795
1737
  Signal,
1796
1738
  Sse,
1797
1739
  SseOut,
1798
1740
  Storage,
1741
+ TABLE_META,
1799
1742
  TooManyRequests,
1800
1743
  TraceId,
1801
1744
  TxPlanError,
@@ -1824,6 +1767,7 @@ export {
1824
1767
  defineError,
1825
1768
  defineMiddleware,
1826
1769
  defineSchema,
1770
+ defineTable,
1827
1771
  enumType,
1828
1772
  getErrorRegistry,
1829
1773
  getHookConfig,
@@ -1832,6 +1776,7 @@ export {
1832
1776
  getRoutes,
1833
1777
  getWebhookConfig,
1834
1778
  inc,
1779
+ installationRef,
1835
1780
  integer,
1836
1781
  isPalbaseExtension,
1837
1782
  jsonb,
@@ -1844,6 +1789,7 @@ export {
1844
1789
  onShutdown,
1845
1790
  onStart,
1846
1791
  openai,
1792
+ ownedByUser,
1847
1793
  ownerOnly,
1848
1794
  policy,
1849
1795
  publicChannel,
@@ -1853,6 +1799,7 @@ export {
1853
1799
  text,
1854
1800
  timestamp,
1855
1801
  toSchemaJSON,
1802
+ userRef,
1856
1803
  uuid,
1857
1804
  vector,
1858
1805
  z