@palbase/backend 12.0.0 → 13.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.
package/dist/index.cjs CHANGED
@@ -1249,7 +1249,13 @@ function baseTsType(def) {
1249
1249
  return "string";
1250
1250
  case "integer":
1251
1251
  return "number";
1252
+ // Both are exact-precision in Postgres and lossy as a JSON number, so the
1253
+ // database proxy serializes them as strings — measured: 9007199254740993
1254
+ // used to arrive as ...992 and 41.00821234567890123 as 41.0082123456789.
1255
+ // numeric had no case at all and fell through to `unknown`, which typed
1256
+ // every numeric column out of existence.
1252
1257
  case "bigint":
1258
+ case "numeric":
1253
1259
  return "string";
1254
1260
  case "boolean":
1255
1261
  return "boolean";
@@ -1768,38 +1774,6 @@ function defineFlags(input) {
1768
1774
  return { __config: FLAGS_CONFIG_KIND, flags };
1769
1775
  }
1770
1776
 
1771
- // src/decorators/controller.ts
1772
- var CONTROLLER_META = /* @__PURE__ */ Symbol.for("palbase.backend.controllerMeta");
1773
- function Controller(basePath, options = {}) {
1774
- return function(ctor) {
1775
- const normalized = basePath.replace(/\/+$/, "");
1776
- if (normalized === "/webhooks" || normalized.startsWith("/webhooks/")) {
1777
- throw new Error(
1778
- `@Controller("${basePath}") uses the reserved /webhooks path \u2014 inbound webhooks are served there`
1779
- );
1780
- }
1781
- const carrier = ctor;
1782
- const meta = {
1783
- __palbase: "controller",
1784
- basePath,
1785
- ...options.auth !== void 0 ? { defaultAuth: options.auth } : {}
1786
- };
1787
- Object.defineProperty(carrier, CONTROLLER_META, {
1788
- value: meta,
1789
- enumerable: false,
1790
- configurable: true,
1791
- writable: false
1792
- });
1793
- Object.defineProperty(carrier, "__palbase", {
1794
- value: "controller",
1795
- enumerable: false,
1796
- configurable: true,
1797
- writable: false
1798
- });
1799
- return ctor;
1800
- };
1801
- }
1802
-
1803
1777
  // src/decorators/registry.ts
1804
1778
  var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
1805
1779
  var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
@@ -1892,6 +1866,48 @@ function getRoutes(ctor) {
1892
1866
  }));
1893
1867
  }
1894
1868
 
1869
+ // src/decorators/controller.ts
1870
+ var CONTROLLER_META = /* @__PURE__ */ Symbol.for("palbase.backend.controllerMeta");
1871
+ var RESERVED_FIRST_SEGMENT = "webhooks";
1872
+ function assertNotReserved(path, subject) {
1873
+ const [first] = path.split("/").filter(Boolean);
1874
+ if (first === RESERVED_FIRST_SEGMENT) {
1875
+ throw new Error(
1876
+ `${subject} resolves under the reserved /${RESERVED_FIRST_SEGMENT} path \u2014 inbound webhooks are served there and would shadow this route`
1877
+ );
1878
+ }
1879
+ }
1880
+ function Controller(basePath, options = {}) {
1881
+ return function(ctor) {
1882
+ assertNotReserved(basePath, `@Controller("${basePath}")`);
1883
+ for (const route of getRoutes(ctor)) {
1884
+ assertNotReserved(
1885
+ `${basePath}${route.subpath}`,
1886
+ `@${route.method}("${route.subpath}") in @Controller("${basePath}")`
1887
+ );
1888
+ }
1889
+ const carrier = ctor;
1890
+ const meta = {
1891
+ __palbase: "controller",
1892
+ basePath,
1893
+ ...options.auth !== void 0 ? { defaultAuth: options.auth } : {}
1894
+ };
1895
+ Object.defineProperty(carrier, CONTROLLER_META, {
1896
+ value: meta,
1897
+ enumerable: false,
1898
+ configurable: true,
1899
+ writable: false
1900
+ });
1901
+ Object.defineProperty(carrier, "__palbase", {
1902
+ value: "controller",
1903
+ enumerable: false,
1904
+ configurable: true,
1905
+ writable: false
1906
+ });
1907
+ return ctor;
1908
+ };
1909
+ }
1910
+
1895
1911
  // src/decorators/methods.ts
1896
1912
  function makeMethodDecorator(method) {
1897
1913
  return function(subpath, options = {}) {
@@ -2217,6 +2233,29 @@ function getWebhookConfig(ctor) {
2217
2233
  "@Webhook requires either a `provider` preset or an explicit `signature` \u2014 an endpoint with no verification would accept forged deliveries"
2218
2234
  );
2219
2235
  }
2236
+ if (meta.provider && meta.signature) {
2237
+ throw new Error(
2238
+ "@Webhook declares BOTH a `provider` preset and an explicit `signature` \u2014 these are alternatives; keep the one that describes the sender, because only `provider` would be used"
2239
+ );
2240
+ }
2241
+ if (meta.signature) {
2242
+ const sig = meta.signature;
2243
+ if (!sig.header) {
2244
+ throw new Error("@Webhook signature requires `header` \u2014 the header the signature arrives in");
2245
+ }
2246
+ if (sig.algo !== "hmac-sha256" && sig.algo !== "hmac-sha1") {
2247
+ throw new Error(`@Webhook signature has an unsupported algo "${sig.algo}"`);
2248
+ }
2249
+ if (sig.encoding !== "hex" && sig.encoding !== "base64") {
2250
+ throw new Error(`@Webhook signature has an unsupported encoding "${sig.encoding}"`);
2251
+ }
2252
+ if (!sig.signs?.includes("{body}")) {
2253
+ throw new Error("@Webhook signature `signs` must contain {body} \u2014 signing a constant is not a signature");
2254
+ }
2255
+ if (sig.signs.includes("{ts}") && !sig.timestampHeader) {
2256
+ throw new Error("@Webhook signature uses {ts} but declares no `timestampHeader` to read it from");
2257
+ }
2258
+ }
2220
2259
  if (!meta.secret?.env) {
2221
2260
  throw new Error('@Webhook requires `secret: { env: "VAR_NAME" }`');
2222
2261
  }