@rebasepro/server 0.9.1-canary.fd3754b → 0.10.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 (50) hide show
  1. package/dist/api/errors.d.ts +16 -1
  2. package/dist/api/rest/api-generator.d.ts +13 -1
  3. package/dist/api/rest/write-validation.d.ts +22 -0
  4. package/dist/api/types.d.ts +2 -2
  5. package/dist/auth/admin-users-route.d.ts +3 -3
  6. package/dist/auth/api-keys/api-key-middleware.d.ts +60 -1
  7. package/dist/auth/api-keys/api-key-permission-guard.d.ts +33 -0
  8. package/dist/auth/api-keys/api-key-types.d.ts +21 -8
  9. package/dist/auth/api-keys/index.d.ts +2 -2
  10. package/dist/auth/auth-hooks.d.ts +7 -7
  11. package/dist/auth/collection-callback-warning.d.ts +21 -0
  12. package/dist/auth/index.d.ts +4 -1
  13. package/dist/auth/interfaces.d.ts +28 -28
  14. package/dist/auth/jwt.d.ts +20 -4
  15. package/dist/auth/magic-link-routes.d.ts +2 -2
  16. package/dist/auth/mfa-routes.d.ts +1 -1
  17. package/dist/auth/middleware.d.ts +24 -3
  18. package/dist/auth/rate-limit-store.d.ts +49 -0
  19. package/dist/auth/rate-limiter.d.ts +68 -6
  20. package/dist/auth/reset-password-admin.d.ts +1 -1
  21. package/dist/auth/session-routes.d.ts +2 -2
  22. package/dist/cron/cron-store.d.ts +18 -0
  23. package/dist/env.d.ts +4 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.es.js +2124 -505
  26. package/dist/index.es.js.map +1 -1
  27. package/dist/init/middlewares.d.ts +1 -0
  28. package/dist/init.d.ts +62 -0
  29. package/dist/{jwt-BwIn8xmk.js → jwt-B3zjddCa.js} +177 -9
  30. package/dist/jwt-B3zjddCa.js.map +1 -0
  31. package/dist/{openapi-generator-Z9oYWLf_.js → openapi-generator-DqSIwNLV.js} +8 -2
  32. package/dist/openapi-generator-DqSIwNLV.js.map +1 -0
  33. package/dist/{src-B4OLmNVa.js → src-CsHhSKbi.js} +60 -2
  34. package/dist/src-CsHhSKbi.js.map +1 -0
  35. package/dist/storage/LocalStorageController.d.ts +19 -2
  36. package/dist/storage/routes.d.ts +8 -1
  37. package/dist/storage/tus-handler.d.ts +21 -1
  38. package/dist/storage/types.d.ts +33 -0
  39. package/dist/utils/compression.d.ts +16 -0
  40. package/dist/utils/sql.d.ts +2 -2
  41. package/package.json +12 -11
  42. package/dist/index.umd.js +0 -48745
  43. package/dist/index.umd.js.map +0 -1
  44. package/dist/jwt-BwIn8xmk.js.map +0 -1
  45. package/dist/ms-DnYXB-Wd.js +0 -162
  46. package/dist/ms-DnYXB-Wd.js.map +0 -1
  47. package/dist/openapi-generator-Z9oYWLf_.js.map +0 -1
  48. package/dist/src-B4OLmNVa.js.map +0 -1
  49. package/dist/src-B9tjYmqP.js +0 -24598
  50. package/dist/src-B9tjYmqP.js.map +0 -1
@@ -4,4 +4,4 @@ import { HonoEnv } from "../api/types";
4
4
  import type { AuthModuleConfig } from "./routes";
5
5
  import { resolveAuthHooks } from "./auth-hooks";
6
6
  import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
7
- export declare function mountMfaRoutes(router: Hono<HonoEnv>, config: AuthModuleConfig, ops: ReturnType<typeof resolveAuthHooks>, parseBody: <T>(schema: z.ZodSchema<T>, body: unknown) => T, applyTransformHook?: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, userId: string) => Promise<AuthResponsePayload>): void;
7
+ export declare function mountMfaRoutes(router: Hono<HonoEnv>, config: AuthModuleConfig, ops: ReturnType<typeof resolveAuthHooks>, parseBody: <T>(schema: z.ZodSchema<T>, body: unknown) => T, applyTransformHook?: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, uid: string) => Promise<AuthResponsePayload>): void;
@@ -1,17 +1,17 @@
1
1
  import { MiddlewareHandler, Context } from "hono";
2
2
  import { DataDriver } from "@rebasepro/types";
3
3
  import { AccessTokenPayload } from "./jwt";
4
- import { HonoEnv } from "../api/types";
4
+ import type { HonoEnv } from "../api/types";
5
5
  import type { ApiKeyStore } from "./api-keys/api-key-store";
6
6
  /**
7
7
  * Result from a custom auth validator.
8
8
  * - `false`/`null`/`undefined` = not authenticated
9
9
  * - `true` = authenticated as default user
10
- * - object with `userId` or `uid` = authenticated with user info
10
+ * - object with `uid` (or legacy `userId`) = authenticated with user info
11
11
  */
12
12
  export type AuthResult = boolean | null | undefined | {
13
- userId?: string;
14
13
  uid?: string;
14
+ userId?: string;
15
15
  roles?: string[];
16
16
  [key: string]: unknown;
17
17
  };
@@ -150,6 +150,27 @@ export declare const queryTokenAuth: MiddlewareHandler<HonoEnv>;
150
150
  * untouched, so they still require a valid token.
151
151
  */
152
152
  export declare const publicObjectAuth: MiddlewareHandler<HonoEnv>;
153
+ /**
154
+ * Helper to match paths for scoped file tokens.
155
+ * Matches exact paths or prefixes (exact folder prefixes or ending with /).
156
+ *
157
+ * The traversal check is defence in depth, not the load-bearing defence.
158
+ *
159
+ * A prefix comparison is a lie for any path containing `..`: `user1/../user2/x`
160
+ * starts with `user1/`, so a token scoped to `user1/` would authorize reading
161
+ * user2's file — and the storage controller would not object, because its own
162
+ * guard stops a path escaping the *bucket*, and that one resolves to `user2/x`,
163
+ * comfortably inside it. The grant is what would be escaped, and this is the
164
+ * only layer that knows what was granted.
165
+ *
166
+ * Nothing arrives here in that shape today: the URL parser resolves `..` (and
167
+ * `%2e%2e`, which the WHATWG spec treats as a dot for normalization) before
168
+ * Hono routes the request, so this comparison already runs on a resolved path.
169
+ * But that is a guarantee of the runtime rather than of this code, and it is
170
+ * one line to not depend on it. The same rule already guards the public-object
171
+ * path — see `isPublicStoragePath`.
172
+ */
173
+ export declare function isPathMatch(requested: string, allowed: string): boolean;
153
174
  /**
154
175
  * Middleware that authenticates file-serving routes using scoped download tokens.
155
176
  * It enforces that only scoped "file-read" tokens can access "/file/*" and "?token=" query params.
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Where a rate limiter keeps its counts.
3
+ *
4
+ * Split out from the limiter itself because the two have different lifetimes:
5
+ * the policy (how many, how often, keyed by what) is a property of the route,
6
+ * and the storage is a property of the deployment. One process behind one
7
+ * proxy is happy with a Map; several replicas behind a load balancer are not,
8
+ * because a limit of 200 enforced independently by four of them is a limit of
9
+ * 800.
10
+ */
11
+ export interface RateLimitDecision {
12
+ allowed: boolean;
13
+ /** Requests left in the current window. Zero when `allowed` is false. */
14
+ remaining: number;
15
+ /** How long until the oldest hit falls out of the window. */
16
+ retryAfterMs: number;
17
+ }
18
+ export interface RateLimitStore {
19
+ /**
20
+ * Record a hit against `key` and say whether it is allowed.
21
+ *
22
+ * Called once per request, so it must not be chatty. Implementations are
23
+ * expected to be atomic per key: two concurrent hits must not both read a
24
+ * count of `limit - 1` and both be allowed.
25
+ */
26
+ hit(key: string, windowMs: number, limit: number): Promise<RateLimitDecision>;
27
+ /** Release timers/connections. Tests need this; servers rarely do. */
28
+ dispose?(): void;
29
+ }
30
+ /**
31
+ * The default store: a sliding window in this process's memory.
32
+ *
33
+ * Sliding rather than fixed: a fixed window lets a caller spend its whole
34
+ * allowance in the last second of one window and again in the first second of
35
+ * the next, which is twice the limit over an instant. Timestamps outside the
36
+ * window are dropped on read, and swept periodically so an idle key does not
37
+ * pin its array forever.
38
+ *
39
+ * Per-process, so N replicas enforce N times the limit between them. That is
40
+ * the honest reason {@link RateLimitStore} exists.
41
+ */
42
+ export declare class MemoryRateLimitStore implements RateLimitStore {
43
+ private store;
44
+ private cleanupInterval?;
45
+ constructor(sweepMs?: number);
46
+ hit(key: string, windowMs: number, limit: number): Promise<RateLimitDecision>;
47
+ private sweep;
48
+ dispose(): void;
49
+ }
@@ -1,5 +1,12 @@
1
1
  import { MiddlewareHandler } from "hono";
2
2
  import { HonoEnv } from "../api/types";
3
+ import { RateLimitStore } from "./rate-limit-store";
4
+ /**
5
+ * Sliding-window rate limiting for Hono.
6
+ *
7
+ * The counting lives in a {@link RateLimitStore} — in this process's memory by
8
+ * default, which is a per-replica limit and says so. See `rate-limit-store.ts`.
9
+ */
3
10
  interface RateLimiterOptions {
4
11
  /** Time window in milliseconds (default: 15 minutes) */
5
12
  windowMs?: number;
@@ -9,13 +16,32 @@ interface RateLimiterOptions {
9
16
  keyGenerator?: (c: Parameters<MiddlewareHandler<HonoEnv>>[0]) => string;
10
17
  /** Custom message for rate limit responses */
11
18
  message?: string;
19
+ /**
20
+ * Where to keep the counts. Defaults to a private in-memory store — pass a
21
+ * shared one to have several limiters (or several processes) agree.
22
+ */
23
+ store?: RateLimitStore;
24
+ /**
25
+ * Per-request limit override, for buckets whose allowance is data rather
26
+ * than config (an API key's own `rate_limit`). Returning `undefined` uses
27
+ * `limit`; returning `null` skips the limiter for this request.
28
+ */
29
+ resolveLimit?: (c: Parameters<MiddlewareHandler<HonoEnv>>[0]) => number | null | undefined;
30
+ /**
31
+ * Number of trusted reverse-proxy hops in front of this server. Each hop
32
+ * appends the address it saw to `X-Forwarded-For`, so the real client IP is
33
+ * the Nth entry from the right. Anything further left is client-supplied and
34
+ * ignored — which is what stops a caller spoofing `X-Forwarded-For` to
35
+ * rotate rate-limit keys. Defaults to `TRUSTED_PROXY_HOPS` (env) or `1`.
36
+ * Set to `0` when the server is exposed directly (no proxy): `X-Forwarded-For`
37
+ * is then ignored entirely in favour of `X-Real-IP`.
38
+ */
39
+ trustedProxyHops?: number;
12
40
  }
13
41
  /**
14
42
  * Create a rate-limiting middleware.
15
43
  *
16
- * Uses a sliding window algorithm: only timestamps within the last
17
- * `windowMs` milliseconds are counted. Old entries are garbage-collected
18
- * every `windowMs` to prevent unbounded memory growth.
44
+ * Uses a sliding window: only hits within the last `windowMs` are counted.
19
45
  */
20
46
  export declare function createRateLimiter(options?: RateLimiterOptions): MiddlewareHandler<HonoEnv>;
21
47
  /**
@@ -36,12 +62,48 @@ export declare const strictAuthLimiter: MiddlewareHandler<HonoEnv>;
36
62
  * via an API key.
37
63
  */
38
64
  export declare function apiKeyKeyGenerator(c: Parameters<MiddlewareHandler<HonoEnv>>[0]): string;
65
+ /** How the data API's limits are apportioned. All fields optional. */
66
+ export interface DataRateLimitConfig {
67
+ /** Turn the whole thing off — for a deployment whose proxy already does it. */
68
+ enabled?: boolean;
69
+ windowMs?: number;
70
+ /** Fallback for an API key with no `rate_limit` of its own. Default 1000. */
71
+ apiKey?: number;
72
+ /** Per signed-in user. Default 1000. */
73
+ user?: number;
74
+ /**
75
+ * Per IP, for requests with no principal at all. Default 300.
76
+ * `null` disables the anonymous bucket entirely — used for routers whose
77
+ * anonymous traffic must not be throttled (public webhook functions).
78
+ */
79
+ anonymous?: number | null;
80
+ /** Share counts across replicas. Defaults to this process's memory. */
81
+ store?: RateLimitStore;
82
+ }
83
+ /**
84
+ * Rate limiting for the data API.
85
+ *
86
+ * Every request is in exactly one bucket, resolved most-specific first: an API
87
+ * key by its id, a signed-in user by their uid, anyone else by IP. Previously
88
+ * only the first of those was limited at all — the middleware returned early
89
+ * for any request without an API key — so JWT and anonymous traffic to
90
+ * `/api/data/*` was unbounded, which is most of the traffic a BaaS gets.
91
+ *
92
+ * Per bucket, not per route: the point is to bound what one caller costs, and
93
+ * they can spend it wherever they like.
94
+ *
95
+ * The defaults are deliberately loose. This is a floor against a runaway client
96
+ * or a naive scraper, not a quota — a deployment that wants real quotas should
97
+ * set them, and one that already has a proxy doing this should pass
98
+ * `enabled: false` rather than pay for it twice.
99
+ */
100
+ export declare function createDataRateLimiter(config?: DataRateLimitConfig): MiddlewareHandler<HonoEnv>;
39
101
  /**
40
102
  * Create a rate limiter specifically for API key requests.
41
103
  *
42
- * When a request is authenticated via an API key that has a `rate_limit`
43
- * configured, this limiter enforces per-key limits using the key's ID
44
- * as the rate limit bucket.
104
+ * @deprecated Use {@link createDataRateLimiter}, which limits signed-in users
105
+ * and anonymous callers too. This one skips every request that is not
106
+ * API-key-authenticated, which was most of them.
45
107
  *
46
108
  * @param defaultLimit - Fallback limit when the key has no `rate_limit` set.
47
109
  * @param windowMs - Time window in milliseconds (default: 15 minutes).
@@ -24,6 +24,6 @@ export interface ResetPasswordRouteConfig {
24
24
  /**
25
25
  * Create a standalone admin route for resetting user passwords.
26
26
  *
27
- * Mounts: POST /users/:userId/reset-password
27
+ * Mounts: POST /users/:uid/reset-password
28
28
  */
29
29
  export declare function createResetPasswordRoute(config: ResetPasswordRouteConfig): Hono<HonoEnv>;
@@ -18,12 +18,12 @@ interface SessionRoutesConfig {
18
18
  isAnonymous?: boolean;
19
19
  metadata?: Record<string, unknown> | null;
20
20
  }, roleIds: string[], accessToken: string, refreshToken: string, providerId: string) => unknown;
21
- createSessionAndTokens: (userId: string, userAgent: string, ipAddress: string) => Promise<{
21
+ createSessionAndTokens: (uid: string, userAgent: string, ipAddress: string) => Promise<{
22
22
  roleIds: string[];
23
23
  accessToken: string;
24
24
  refreshToken: string;
25
25
  }>;
26
- applyTransformHook: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, userId: string) => Promise<AuthResponsePayload>;
26
+ applyTransformHook: (response: AuthResponsePayload, method: TransformAuthResponseContext["method"], request: Request, uid: string) => Promise<AuthResponsePayload>;
27
27
  }
28
28
  export declare function mountSessionRoutes(opts: SessionRoutesConfig): void;
29
29
  export {};
@@ -28,5 +28,23 @@ export interface CronStore {
28
28
  totalFailures: number;
29
29
  lastRunAt?: string;
30
30
  }>>;
31
+ /**
32
+ * Atomically claim a scheduled run slot for a job.
33
+ *
34
+ * `slot` is the *scheduled* fire time (ISO string) derived from the cron
35
+ * expression — deterministic across instances regardless of timer drift,
36
+ * so all instances contend on the same (jobId, slot) key. Exactly one
37
+ * caller wins the insert against the unique constraint and executes;
38
+ * the rest skip.
39
+ *
40
+ * Fails open (returns true) on unexpected store errors, so a broken
41
+ * claims table degrades to uncoordinated execution rather than silently
42
+ * never running jobs.
43
+ *
44
+ * Optional so custom stores written against the pre-claims interface
45
+ * keep working — the scheduler treats a missing implementation as
46
+ * uncoordinated (always run).
47
+ */
48
+ tryClaimRun?(jobId: string, slot: string): Promise<boolean>;
31
49
  }
32
50
  export declare function createCronStore(driver: DataDriver): CronStore | undefined;
package/dist/env.d.ts CHANGED
@@ -42,6 +42,7 @@ declare const rebaseEnvSchema: z.ZodObject<{
42
42
  STORAGE_TYPE: z.ZodDefault<z.ZodEnum<{
43
43
  local: "local";
44
44
  s3: "s3";
45
+ gcs: "gcs";
45
46
  }>>;
46
47
  STORAGE_PATH: z.ZodOptional<z.ZodString>;
47
48
  S3_BUCKET: z.ZodOptional<z.ZodString>;
@@ -54,6 +55,9 @@ declare const rebaseEnvSchema: z.ZodObject<{
54
55
  true: "true";
55
56
  false: "false";
56
57
  }>>, z.ZodTransform<boolean, "" | "true" | "false" | undefined>>;
58
+ GCS_BUCKET: z.ZodOptional<z.ZodString>;
59
+ GCS_PROJECT_ID: z.ZodOptional<z.ZodString>;
60
+ GCS_KEY_FILENAME: z.ZodOptional<z.ZodString>;
57
61
  }, z.core.$strip>;
58
62
  /** Inferred type of the validated environment. */
59
63
  export type RebaseEnv = z.infer<typeof rebaseEnvSchema>;
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * by specialized driver packages like `@rebasepro/server-postgres`.
8
8
  */
9
9
  export { initializeRebaseBackend, isAuthAdapter, isDatabaseAdapter } from "./init";
10
- export type { RebaseBackendConfig, RebaseBackendInstance, RebaseAuthConfig } from "./init";
10
+ export type { RebaseBackendConfig, RebaseBackendInstance, RebaseAuthConfig, BaasOptions } from "./init";
11
11
  export { rebase, _setRebaseMock, _resetRebaseMock } from "./singleton";
12
12
  export { loadCollectionsFromDirectory, applyCollectionDefaults, type CollectionDefaults } from "./collections/loader";
13
13
  export * from "./db/interfaces";