@palbase/backend 24.3.0 → 25.0.1

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 +103 -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-34I4GB7D.js} +82 -49
  6. package/dist/chunk-34I4GB7D.js.map +1 -0
  7. package/dist/{chunk-UWSYTUGM.js → chunk-35PNTIRN.js} +48 -1
  8. package/dist/chunk-35PNTIRN.js.map +1 -0
  9. package/dist/chunk-HBOJLP2Z.js +840 -0
  10. package/dist/chunk-HBOJLP2Z.js.map +1 -0
  11. package/dist/{chunk-7Z6MGMXQ.js → chunk-XJ2RSHEU.js} +11 -5
  12. package/dist/chunk-XJ2RSHEU.js.map +1 -0
  13. package/dist/{chunk-ERDL5VAE.js → chunk-YOY5DFQS.js} +2 -2
  14. package/dist/db/env.cjs.map +1 -1
  15. package/dist/db/env.d.cts +31 -14
  16. package/dist/db/env.d.ts +31 -14
  17. package/dist/db/index.cjs +226 -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 +89 -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-BTMYod_l.d.ts → index-B4CcpqLb.d.ts} +224 -75
  28. package/dist/{index-DEneI8Mn.d.ts → index-B8v6hVyU.d.ts} +5 -2
  29. package/dist/{index-BLAbr9ZH.d.cts → index-DmVyY6N7.d.cts} +224 -75
  30. package/dist/{index-C-ALG22n.d.cts → index-VsjBQ4Kw.d.cts} +5 -2
  31. package/dist/index.cjs +580 -301
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +125 -22
  34. package/dist/index.d.ts +125 -22
  35. package/dist/index.js +173 -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 +430 -153
  44. package/docs/schema.md +303 -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-YOY5DFQS.js.map} +0 -0
package/dist/index.js CHANGED
@@ -2,27 +2,34 @@ 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,
20
+ qualifiedTableKey,
16
21
  raw,
17
22
  text,
18
23
  timestamp,
24
+ toSchemaJSON,
25
+ userRef,
19
26
  uuid,
20
27
  vector
21
- } from "./chunk-D5CQES25.js";
28
+ } from "./chunk-HBOJLP2Z.js";
22
29
  import {
23
30
  defineError,
24
31
  getErrorRegistry
25
- } from "./chunk-ERDL5VAE.js";
32
+ } from "./chunk-YOY5DFQS.js";
26
33
  import {
27
34
  Cache,
28
35
  Database,
@@ -41,7 +48,7 @@ import {
41
48
  __setRuntime,
42
49
  onShutdown,
43
50
  onStart
44
- } from "./chunk-7Z6MGMXQ.js";
51
+ } from "./chunk-XJ2RSHEU.js";
45
52
  import {
46
53
  TxPlanError,
47
54
  TxRefError,
@@ -67,10 +74,13 @@ import {
67
74
  getRegisteredControllers,
68
75
  getRoutes,
69
76
  recordParam,
77
+ recordRoom,
78
+ recordRoomHook,
79
+ recordRoomMessage,
70
80
  recordRoute,
71
81
  recordThrows,
72
82
  resolveEffectiveAuth
73
- } from "./chunk-UWSYTUGM.js";
83
+ } from "./chunk-35PNTIRN.js";
74
84
  import "./chunk-7D4SUZUM.js";
75
85
 
76
86
  // src/clients/http.ts
@@ -948,168 +958,6 @@ function buildModuleClients(cfg) {
948
958
  };
949
959
  }
950
960
 
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
961
  // src/channels.ts
1114
962
  var CHANNELS = /* @__PURE__ */ Symbol.for("palbase.backend.channels");
1115
963
  function ownerOnly() {
@@ -1209,50 +1057,82 @@ function rowType(def) {
1209
1057
  function optionalOnInsert(def) {
1210
1058
  return def.nullable === true || def.defaultRandom === true || def.defaultNow === true || def.defaultValue !== void 0;
1211
1059
  }
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;
1060
+ function forwardName(column) {
1061
+ return column.endsWith("_id") ? column.slice(0, -3) : column;
1218
1062
  }
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 });
1233
- }
1063
+ function describeOrigin(o) {
1064
+ return o.direction === "forward" ? `the foreign key "${o.table}.${o.column}"` : `the reverse of "${o.table}.${o.column}"`;
1065
+ }
1066
+ function renameCall(o) {
1067
+ if (o.direction === "reverse") return `references(() => \u2026, { reverseAs: "\u2026" })`;
1068
+ if (o.def.owns === true) {
1069
+ return `ownedByUser() takes no { as } \u2014 if "${o.column}" only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead`;
1234
1070
  }
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);
1071
+ const target = o.def.references?.table;
1072
+ if (target === "auth.users") return `userRef({ onDelete: \u2026, as: "\u2026" })`;
1073
+ if (target === "auth.installations") return `installationRef({ onDelete: \u2026, as: "\u2026" })`;
1074
+ if (o.def.selfRefColumn !== void 0) return `selfReferences("\u2026", { as: "\u2026" })`;
1075
+ return `references(() => \u2026, { as: "\u2026" })`;
1076
+ }
1077
+ function buildRelations(schemas) {
1078
+ const out = /* @__PURE__ */ new Map();
1079
+ const taken = /* @__PURE__ */ new Map();
1080
+ for (const schema of schemas) {
1081
+ for (const t of Object.values(schema.tables)) {
1082
+ const key = qualifiedTableKey(schema.name, t.name);
1083
+ out.set(key, []);
1084
+ taken.set(key, /* @__PURE__ */ new Map());
1247
1085
  }
1248
- state.set(table, "done");
1249
1086
  }
1250
- for (const table of raw2.keys()) {
1251
- if (state.get(table) === void 0) visit(table);
1087
+ const claim = (tableKey, name, origin, edge) => {
1088
+ const names = taken.get(tableKey);
1089
+ if (names === void 0) return;
1090
+ const held = names.get(name);
1091
+ if (held !== void 0) {
1092
+ if (held.def.owns === true && origin.def.owns === true) {
1093
+ throw new Error(
1094
+ `table "${tableKey}" declares TWO owner columns (${held.column}, ${origin.column}) \u2014 a table has at most ONE ownedByUser(). If the other column only POINTS at a user, declare it userRef({ onDelete: \u2026 }) instead.`
1095
+ );
1096
+ }
1097
+ const heldFix = renameCall(held);
1098
+ const originFix = renameCall(origin);
1099
+ const remedy = heldFix === originFix ? `rename one of them: ${heldFix}` : `rename one of them \u2014 "${held.table}.${held.column}": ${heldFix}; "${origin.table}.${origin.column}": ${originFix}`;
1100
+ throw new Error(
1101
+ `table "${tableKey}": ${describeOrigin(held)} and ${describeOrigin(origin)} both resolve to the relation name "${name}" \u2014 ${remedy}.`
1102
+ );
1103
+ }
1104
+ names.set(name, origin);
1105
+ out.get(tableKey)?.push(edge);
1106
+ };
1107
+ for (const schema of schemas) {
1108
+ for (const table of Object.values(schema.tables)) {
1109
+ const childKey = qualifiedTableKey(schema.name, table.name);
1110
+ if (!out.has(childKey)) continue;
1111
+ for (const [col, builder] of Object.entries(table.columns)) {
1112
+ const def = builder._def;
1113
+ if (def.references === void 0) continue;
1114
+ const targetKey = def.references.table;
1115
+ const name = def.owns === true ? "owner" : def.refAs ?? forwardName(col);
1116
+ claim(
1117
+ childKey,
1118
+ name,
1119
+ { table: table.name, column: col, direction: "forward", def },
1120
+ { name, to: targetKey, kind: "one", via: col }
1121
+ );
1122
+ if (!out.has(targetKey) || targetKey === childKey) continue;
1123
+ const reverse = def.reverseAs ?? table.name;
1124
+ claim(
1125
+ targetKey,
1126
+ reverse,
1127
+ { table: table.name, column: col, direction: "reverse", def },
1128
+ { name: reverse, to: childKey, kind: "many", via: col }
1129
+ );
1130
+ }
1131
+ }
1252
1132
  }
1253
- return pruned;
1133
+ return out;
1254
1134
  }
1255
- function tableBlock(table, children, indent) {
1135
+ function tableBlock(table, relations, indent) {
1256
1136
  const cols = Object.entries(table.columns);
1257
1137
  const rowLines = cols.map(([col, builder]) => {
1258
1138
  return `${indent} ${col}: ${rowType(builder._def)};`;
@@ -1262,8 +1142,9 @@ function tableBlock(table, children, indent) {
1262
1142
  const opt = optionalOnInsert(def) ? "?" : "";
1263
1143
  return `${indent} ${col}${opt}: ${rowType(def)};`;
1264
1144
  });
1265
- const owner = ownerColumnOf(table);
1266
- const childEntries = children.map((e) => `${JSON.stringify(e.child)}: ${JSON.stringify(e.column)}`).join("; ");
1145
+ const relEntries = relations.map(
1146
+ (r) => `${r.name}: { to: ${JSON.stringify(r.to)}; kind: ${JSON.stringify(r.kind)}; via: ${JSON.stringify(r.via)} }`
1147
+ ).join("; ");
1267
1148
  return [
1268
1149
  `${indent}${table.name}: {`,
1269
1150
  `${indent} row: {`,
@@ -1272,8 +1153,7 @@ function tableBlock(table, children, indent) {
1272
1153
  `${indent} insert: {`,
1273
1154
  ...insertLines,
1274
1155
  `${indent} };`,
1275
- `${indent} owner: ${owner === null ? "null" : JSON.stringify(owner)};`,
1276
- `${indent} children: {${childEntries === "" ? "" : ` ${childEntries} `}};`,
1156
+ `${indent} relations: {${relEntries === "" ? "" : ` ${relEntries} `}};`,
1277
1157
  // searchable: vector kolonu YA DA search beyanı → EnvTypedTable'da search()
1278
1158
  // üyesini açan yapısal bayrak (FR-013). Yokken satır hiç üretilmez ki
1279
1159
  // mevcut şemaların d.ts'i bayt-aynı kalsın.
@@ -1281,22 +1161,36 @@ function tableBlock(table, children, indent) {
1281
1161
  `${indent}};`
1282
1162
  ].join("\n");
1283
1163
  }
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) ?? [], " ")
1164
+ function makeEnvDts(schemas) {
1165
+ const relations = buildRelations(schemas);
1166
+ const publicSchema = schemas.find((s) => s.name === "public");
1167
+ const others = schemas.filter((s) => s.name !== "public");
1168
+ const publicBlocks = Object.keys(publicSchema?.tables ?? {}).map(
1169
+ (name) => tableBlock(publicSchema.tables[name], relations.get(name) ?? [], " ")
1289
1170
  );
1290
- const body = blocks.length > 0 ? `
1291
- ${blocks.join("\n")}
1171
+ const body = publicBlocks.length > 0 ? `
1172
+ ${publicBlocks.join("\n")}
1173
+ ` : "";
1174
+ const schemaBlocks = others.map((schema) => {
1175
+ const inner = Object.keys(schema.tables).map(
1176
+ (name) => tableBlock(schema.tables[name], relations.get(qualifiedTableKey(schema.name, name)) ?? [], " ")
1177
+ );
1178
+ return ` ${schema.name}: {
1179
+ ${inner.join("\n")}
1180
+ };`;
1181
+ });
1182
+ const schemasBody = schemaBlocks.length > 0 ? `
1183
+ ${schemaBlocks.join("\n")}
1292
1184
  ` : "";
1293
1185
  return `// AUTO-GENERATED by @palbase/backend \u2014 DO NOT EDIT.
1294
- // Regenerated from db/schema.ts by \`palbase build\` and by every deploy.
1186
+ // Regenerated from db/*.ts by \`palbase build\` and by every deploy.
1295
1187
  // Augments the @palbase/backend/env \`Tables\` interface so \`Database.tables.*\`
1296
- // is typed with no import and no generic.
1188
+ // is typed with no import and no generic. Schemas other than \`public\` land
1189
+ // under \`Schemas\`, reached with \`Database.schema("<name>").tables.*\`.
1297
1190
 
1298
1191
  declare module "@palbase/backend/env" {
1299
1192
  interface Tables {${body}}
1193
+ interface Schemas {${schemasBody}}
1300
1194
  }
1301
1195
 
1302
1196
  export {};
@@ -1577,6 +1471,56 @@ function Signal() {
1577
1471
  };
1578
1472
  }
1579
1473
 
1474
+ // src/decorators/room.ts
1475
+ var DEFAULT_GRACE_MS = 6e4;
1476
+ function assertPattern(pattern) {
1477
+ if (pattern.includes("/")) {
1478
+ throw new Error(
1479
+ `@Room("${pattern}"): channel patterns are colon-separated, not slash-separated. Write "${pattern.replace(/\//g, ":")}" \u2014 a slash matches no live topic.`
1480
+ );
1481
+ }
1482
+ const segments = pattern.split(":");
1483
+ if (segments.some((s) => s === "")) {
1484
+ throw new Error(`@Room("${pattern}"): empty segment \u2014 every segment must be non-empty.`);
1485
+ }
1486
+ const seen = /* @__PURE__ */ new Set();
1487
+ for (const s of segments) {
1488
+ if (!s.startsWith("{") || !s.endsWith("}")) continue;
1489
+ const name = s.slice(1, -1);
1490
+ if (name === "") throw new Error(`@Room("${pattern}"): "{}" names no parameter.`);
1491
+ if (seen.has(name)) {
1492
+ throw new Error(`@Room("${pattern}"): parameter "{${name}}" appears twice.`);
1493
+ }
1494
+ seen.add(name);
1495
+ }
1496
+ }
1497
+ function Room(pattern, options) {
1498
+ return function(ctor) {
1499
+ assertPattern(pattern);
1500
+ recordRoom(ctor, {
1501
+ pattern,
1502
+ events: options.events,
1503
+ graceMs: options.graceMs ?? DEFAULT_GRACE_MS
1504
+ });
1505
+ return ctor;
1506
+ };
1507
+ }
1508
+ function hookDecorator(hook) {
1509
+ return () => function(target, propertyKey) {
1510
+ recordRoomHook(target, hook, String(propertyKey));
1511
+ };
1512
+ }
1513
+ var OnAuthorize = hookDecorator("authorize");
1514
+ var OnFirst = hookDecorator("first");
1515
+ var OnJoin = hookDecorator("join");
1516
+ var OnLeave = hookDecorator("leave");
1517
+ var OnEmpty = hookDecorator("empty");
1518
+ function OnMessage(name, schema) {
1519
+ return function(target, propertyKey) {
1520
+ recordRoomMessage(target, name, String(propertyKey), schema);
1521
+ };
1522
+ }
1523
+
1580
1524
  // src/decorators/params.ts
1581
1525
  function makeParamDecorator(kind, extra) {
1582
1526
  return function(target, propertyKey, parameterIndex) {
@@ -1777,6 +1721,12 @@ export {
1777
1721
  NotFound,
1778
1722
  Notifications,
1779
1723
  On,
1724
+ OnAuthorize,
1725
+ OnEmpty,
1726
+ OnFirst,
1727
+ OnJoin,
1728
+ OnLeave,
1729
+ OnMessage,
1780
1730
  OptionalUser,
1781
1731
  PALBASE_EXTENSIONS,
1782
1732
  PalError,
@@ -1791,11 +1741,13 @@ export {
1791
1741
  Realtime,
1792
1742
  Req,
1793
1743
  RequestId,
1744
+ Room,
1794
1745
  Secrets,
1795
1746
  Signal,
1796
1747
  Sse,
1797
1748
  SseOut,
1798
1749
  Storage,
1750
+ TABLE_META,
1799
1751
  TooManyRequests,
1800
1752
  TraceId,
1801
1753
  TxPlanError,
@@ -1824,6 +1776,7 @@ export {
1824
1776
  defineError,
1825
1777
  defineMiddleware,
1826
1778
  defineSchema,
1779
+ defineTable,
1827
1780
  enumType,
1828
1781
  getErrorRegistry,
1829
1782
  getHookConfig,
@@ -1832,6 +1785,7 @@ export {
1832
1785
  getRoutes,
1833
1786
  getWebhookConfig,
1834
1787
  inc,
1788
+ installationRef,
1835
1789
  integer,
1836
1790
  isPalbaseExtension,
1837
1791
  jsonb,
@@ -1844,6 +1798,7 @@ export {
1844
1798
  onShutdown,
1845
1799
  onStart,
1846
1800
  openai,
1801
+ ownedByUser,
1847
1802
  ownerOnly,
1848
1803
  policy,
1849
1804
  publicChannel,
@@ -1853,6 +1808,7 @@ export {
1853
1808
  text,
1854
1809
  timestamp,
1855
1810
  toSchemaJSON,
1811
+ userRef,
1856
1812
  uuid,
1857
1813
  vector,
1858
1814
  z