@rebasepro/server-core 0.6.1 → 0.7.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 (165) hide show
  1. package/package.json +14 -14
  2. package/src/api/errors.ts +16 -2
  3. package/src/api/graphql/graphql-schema-generator.ts +45 -10
  4. package/src/api/server.ts +10 -2
  5. package/src/api/types.ts +6 -0
  6. package/src/auth/adapter-middleware.ts +10 -2
  7. package/src/auth/admin-roles-route.ts +36 -0
  8. package/src/auth/admin-users-route.ts +302 -0
  9. package/src/auth/api-keys/api-key-middleware.ts +4 -3
  10. package/src/auth/api-keys/api-key-routes.ts +12 -2
  11. package/src/auth/api-keys/api-key-store.ts +83 -66
  12. package/src/auth/api-keys/api-key-types.ts +8 -0
  13. package/src/auth/apple-oauth.ts +2 -1
  14. package/src/auth/auth-hooks.ts +21 -0
  15. package/src/auth/bitbucket-oauth.ts +2 -1
  16. package/src/auth/builtin-auth-adapter.ts +28 -4
  17. package/src/auth/custom-auth-adapter.ts +2 -0
  18. package/src/auth/discord-oauth.ts +2 -1
  19. package/src/auth/facebook-oauth.ts +2 -1
  20. package/src/auth/github-oauth.ts +2 -1
  21. package/src/auth/gitlab-oauth.ts +2 -1
  22. package/src/auth/google-oauth.ts +8 -4
  23. package/src/auth/index.ts +2 -0
  24. package/src/auth/interfaces.ts +27 -0
  25. package/src/auth/linkedin-oauth.ts +2 -1
  26. package/src/auth/magic-link-routes.ts +167 -0
  27. package/src/auth/mfa-crypto.ts +91 -0
  28. package/src/auth/mfa-routes.ts +34 -10
  29. package/src/auth/microsoft-oauth.ts +2 -1
  30. package/src/auth/middleware.ts +10 -1
  31. package/src/auth/reset-password-admin.ts +17 -1
  32. package/src/auth/routes.ts +77 -8
  33. package/src/auth/session-routes.ts +15 -3
  34. package/src/auth/slack-oauth.ts +2 -1
  35. package/src/auth/spotify-oauth.ts +2 -1
  36. package/src/auth/twitter-oauth.ts +8 -1
  37. package/src/cron/cron-store.ts +25 -23
  38. package/src/email/index.ts +3 -2
  39. package/src/email/templates.ts +82 -0
  40. package/src/email/types.ts +16 -0
  41. package/src/init.ts +137 -5
  42. package/src/services/routed-realtime-service.ts +113 -0
  43. package/src/utils/dev-port.ts +13 -7
  44. package/test/auth-routes.test.ts +54 -4
  45. package/test/custom-auth-adapter.test.ts +20 -1
  46. package/test/env.test.ts +9 -19
  47. package/test/multi-datasource-routing.test.ts +113 -0
  48. package/test/routed-realtime-service.test.ts +86 -0
  49. package/test/transform-auth-response.test.ts +305 -0
  50. package/build-errors.txt +0 -52
  51. package/dist/api/ast-schema-editor.d.ts +0 -21
  52. package/dist/api/collections_for_test/callbacks_test_collection.d.ts +0 -2
  53. package/dist/api/errors.d.ts +0 -53
  54. package/dist/api/graphql/graphql-schema-generator.d.ts +0 -35
  55. package/dist/api/graphql/index.d.ts +0 -1
  56. package/dist/api/index.d.ts +0 -9
  57. package/dist/api/logs-routes.d.ts +0 -37
  58. package/dist/api/openapi-generator.d.ts +0 -16
  59. package/dist/api/rest/api-generator.d.ts +0 -88
  60. package/dist/api/rest/index.d.ts +0 -1
  61. package/dist/api/rest/query-parser.d.ts +0 -11
  62. package/dist/api/schema-editor-routes.d.ts +0 -3
  63. package/dist/api/server.d.ts +0 -40
  64. package/dist/api/types.d.ts +0 -98
  65. package/dist/auth/adapter-middleware.d.ts +0 -37
  66. package/dist/auth/admin-user-ops.d.ts +0 -79
  67. package/dist/auth/api-keys/api-key-middleware.d.ts +0 -39
  68. package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -32
  69. package/dist/auth/api-keys/api-key-routes.d.ts +0 -20
  70. package/dist/auth/api-keys/api-key-store.d.ts +0 -35
  71. package/dist/auth/api-keys/api-key-types.d.ts +0 -88
  72. package/dist/auth/api-keys/index.d.ts +0 -17
  73. package/dist/auth/apple-oauth.d.ts +0 -30
  74. package/dist/auth/auth-hooks.d.ts +0 -225
  75. package/dist/auth/bitbucket-oauth.d.ts +0 -11
  76. package/dist/auth/builtin-auth-adapter.d.ts +0 -49
  77. package/dist/auth/crypto-utils.d.ts +0 -16
  78. package/dist/auth/custom-auth-adapter.d.ts +0 -39
  79. package/dist/auth/discord-oauth.d.ts +0 -14
  80. package/dist/auth/facebook-oauth.d.ts +0 -14
  81. package/dist/auth/github-oauth.d.ts +0 -15
  82. package/dist/auth/gitlab-oauth.d.ts +0 -13
  83. package/dist/auth/google-oauth.d.ts +0 -47
  84. package/dist/auth/index.d.ts +0 -36
  85. package/dist/auth/interfaces.d.ts +0 -401
  86. package/dist/auth/jwt.d.ts +0 -55
  87. package/dist/auth/linkedin-oauth.d.ts +0 -18
  88. package/dist/auth/mfa-routes.d.ts +0 -6
  89. package/dist/auth/mfa.d.ts +0 -49
  90. package/dist/auth/microsoft-oauth.d.ts +0 -16
  91. package/dist/auth/middleware.d.ts +0 -106
  92. package/dist/auth/password.d.ts +0 -22
  93. package/dist/auth/rate-limiter.d.ts +0 -50
  94. package/dist/auth/reset-password-admin.d.ts +0 -29
  95. package/dist/auth/rls-scope.d.ts +0 -31
  96. package/dist/auth/routes.d.ts +0 -33
  97. package/dist/auth/session-routes.d.ts +0 -25
  98. package/dist/auth/slack-oauth.d.ts +0 -12
  99. package/dist/auth/spotify-oauth.d.ts +0 -12
  100. package/dist/auth/twitter-oauth.d.ts +0 -18
  101. package/dist/backend-CIxN4FVm.js +0 -15
  102. package/dist/backend-CIxN4FVm.js.map +0 -1
  103. package/dist/chunk-Dze3rakg.js +0 -42
  104. package/dist/collections/BackendCollectionRegistry.d.ts +0 -13
  105. package/dist/collections/loader.d.ts +0 -5
  106. package/dist/cron/cron-loader.d.ts +0 -17
  107. package/dist/cron/cron-routes.d.ts +0 -14
  108. package/dist/cron/cron-scheduler.d.ts +0 -106
  109. package/dist/cron/cron-store.d.ts +0 -32
  110. package/dist/cron/index.d.ts +0 -6
  111. package/dist/db/interfaces.d.ts +0 -18
  112. package/dist/dist-CZKP-Xz4.js +0 -832
  113. package/dist/dist-CZKP-Xz4.js.map +0 -1
  114. package/dist/email/index.d.ts +0 -6
  115. package/dist/email/smtp-email-service.d.ts +0 -25
  116. package/dist/email/templates.d.ts +0 -42
  117. package/dist/email/types.d.ts +0 -108
  118. package/dist/env.d.ts +0 -102
  119. package/dist/from-VbwD7xRf.js +0 -3849
  120. package/dist/from-VbwD7xRf.js.map +0 -1
  121. package/dist/functions/function-loader.d.ts +0 -17
  122. package/dist/functions/function-routes.d.ts +0 -10
  123. package/dist/functions/index.d.ts +0 -3
  124. package/dist/history/history-routes.d.ts +0 -23
  125. package/dist/history/index.d.ts +0 -1
  126. package/dist/index.d.ts +0 -33
  127. package/dist/index.es.js +0 -61731
  128. package/dist/index.es.js.map +0 -1
  129. package/dist/index.umd.js +0 -73221
  130. package/dist/index.umd.js.map +0 -1
  131. package/dist/init/docs.d.ts +0 -4
  132. package/dist/init/health.d.ts +0 -2
  133. package/dist/init/middlewares.d.ts +0 -10
  134. package/dist/init/shutdown.d.ts +0 -11
  135. package/dist/init/storage.d.ts +0 -5
  136. package/dist/init.d.ts +0 -258
  137. package/dist/jwt-DHcQRGC3.js +0 -4168
  138. package/dist/jwt-DHcQRGC3.js.map +0 -1
  139. package/dist/logger-BYU66ENZ.js +0 -94
  140. package/dist/logger-BYU66ENZ.js.map +0 -1
  141. package/dist/ms-BeBSuOXN.js +0 -125
  142. package/dist/ms-BeBSuOXN.js.map +0 -1
  143. package/dist/multipart-parser-CedBDOeC.js +0 -299
  144. package/dist/multipart-parser-CedBDOeC.js.map +0 -1
  145. package/dist/serve-spa.d.ts +0 -30
  146. package/dist/services/driver-registry.d.ts +0 -78
  147. package/dist/services/webhook-service.d.ts +0 -29
  148. package/dist/singleton.d.ts +0 -35
  149. package/dist/src-COaj0G3P.js +0 -1182
  150. package/dist/src-COaj0G3P.js.map +0 -1
  151. package/dist/storage/LocalStorageController.d.ts +0 -46
  152. package/dist/storage/S3StorageController.d.ts +0 -36
  153. package/dist/storage/image-transform.d.ts +0 -50
  154. package/dist/storage/index.d.ts +0 -28
  155. package/dist/storage/routes.d.ts +0 -38
  156. package/dist/storage/storage-registry.d.ts +0 -78
  157. package/dist/storage/tus-handler.d.ts +0 -51
  158. package/dist/storage/types.d.ts +0 -103
  159. package/dist/types/index.d.ts +0 -11
  160. package/dist/utils/dev-port.d.ts +0 -35
  161. package/dist/utils/logger.d.ts +0 -31
  162. package/dist/utils/logging.d.ts +0 -9
  163. package/dist/utils/request-id.d.ts +0 -4
  164. package/dist/utils/request-logger.d.ts +0 -19
  165. package/dist/utils/sql.d.ts +0 -27
@@ -0,0 +1,305 @@
1
+ /**
2
+ * transformAuthResponse Hook — Integration Tests
3
+ *
4
+ * Tests the `transformAuthResponse` hook across login, register, refresh,
5
+ * and error handling scenarios. Services are mocked following the same
6
+ * pattern as auth-routes.test.ts.
7
+ */
8
+
9
+ import { Hono } from "hono";
10
+ import type { HonoEnv } from "../src/api/types";
11
+ import { errorHandler } from "../src/api/errors";
12
+ import { createAuthRoutes, AuthModuleConfig } from "../src/auth/routes";
13
+ import type { AuthRepository } from "../src/auth/interfaces";
14
+ import { configureJwt, hashRefreshToken } from "../src/auth/jwt";
15
+ import type { AuthHooks } from "../src/auth/auth-hooks";
16
+ import type { AuthResponsePayload, TransformAuthResponseContext } from "@rebasepro/types";
17
+
18
+ // ── Mocks ───────────────────────────────────────────────────────────────────
19
+
20
+ jest.mock("../src/auth/password");
21
+
22
+ jest.mock("../src/utils/logger", () => {
23
+ return {
24
+ logger: {
25
+ info: jest.fn(),
26
+ warn: jest.fn(),
27
+ error: jest.fn(),
28
+ debug: jest.fn(),
29
+ child: jest.fn().mockReturnThis()
30
+ }
31
+ };
32
+ });
33
+
34
+ // Bypass rate limiters — they share state across tests and cause 429s
35
+ jest.mock("../src/auth/rate-limiter", () => {
36
+ const passthrough = async (_c: unknown, next: () => Promise<void>) => next();
37
+ return {
38
+ createRateLimiter: () => passthrough,
39
+ defaultAuthLimiter: passthrough,
40
+ strictAuthLimiter: passthrough
41
+ };
42
+ });
43
+
44
+ import { validatePasswordStrength, hashPassword, verifyPassword } from "../src/auth/password";
45
+ import { logger } from "../src/utils/logger";
46
+
47
+ // ── Helpers ─────────────────────────────────────────────────────────────────
48
+
49
+ const TEST_SECRET = "integration-test-secret-key-that-is-definitely-32-chars-long!!";
50
+
51
+ function mockUser(overrides: Partial<{ id: string; email: string; passwordHash: string | null; displayName: string | null; photoUrl: string | null; provider: string; emailVerified: boolean; emailVerificationToken: string | null }> = {}) {
52
+ return {
53
+ id: overrides.id ?? "user-1",
54
+ email: overrides.email ?? "test@example.com",
55
+ passwordHash: "passwordHash" in overrides ? overrides.passwordHash : "salt:hash",
56
+ displayName: overrides.displayName ?? "Test User",
57
+ photoUrl: overrides.photoUrl ?? null,
58
+ emailVerified: overrides.emailVerified ?? false,
59
+ emailVerificationToken: overrides.emailVerificationToken ?? null,
60
+ emailVerificationSentAt: null,
61
+ createdAt: new Date(),
62
+ updatedAt: new Date()
63
+ };
64
+ }
65
+
66
+ function mockRole(id: string, isAdmin = false) {
67
+ return { id,
68
+ name: id.charAt(0).toUpperCase() + id.slice(1),
69
+ isAdmin,
70
+ defaultPermissions: null,
71
+ collectionPermissions: null };
72
+ }
73
+
74
+ let mockAuthRepo: jest.Mocked<AuthRepository>;
75
+
76
+ function createApp(opts: { authHooks?: AuthHooks } = {}) {
77
+ mockAuthRepo = {
78
+ getUserByEmail: jest.fn().mockResolvedValue(null),
79
+ getUserByIdentity: jest.fn().mockResolvedValue(null),
80
+ linkUserIdentity: jest.fn().mockResolvedValue(undefined),
81
+ getUserIdentities: jest.fn().mockResolvedValue([]),
82
+ getUserById: jest.fn().mockResolvedValue(null),
83
+ createUser: jest.fn().mockImplementation((data) =>
84
+ Promise.resolve(mockUser({ email: data.email,
85
+ displayName: data.displayName,
86
+ passwordHash: data.passwordHash }))
87
+ ),
88
+ listUsers: jest.fn().mockResolvedValue([]),
89
+ getUserRoles: jest.fn().mockResolvedValue([mockRole("editor")]),
90
+ getUserRoleIds: jest.fn().mockResolvedValue(["editor"]),
91
+ assignDefaultRole: jest.fn().mockResolvedValue(undefined),
92
+ setUserRoles: jest.fn().mockResolvedValue(undefined),
93
+ updateUser: jest.fn().mockImplementation((id, data) =>
94
+ Promise.resolve(mockUser({ id,
95
+ ...data }))
96
+ ),
97
+ deleteUser: jest.fn().mockResolvedValue(undefined),
98
+ updatePassword: jest.fn().mockResolvedValue(undefined),
99
+ setEmailVerified: jest.fn().mockResolvedValue(undefined),
100
+ setVerificationToken: jest.fn().mockResolvedValue(undefined),
101
+ getUserByVerificationToken: jest.fn().mockResolvedValue(null),
102
+ getUserWithRoles: jest.fn().mockImplementation(async (userId) => {
103
+ const user = mockUser({ id: userId });
104
+ return { user,
105
+ roles: [mockRole("editor")] };
106
+ }),
107
+ createRefreshToken: jest.fn().mockResolvedValue(undefined),
108
+ findRefreshTokenByHash: jest.fn().mockResolvedValue(null),
109
+ deleteRefreshToken: jest.fn().mockResolvedValue(undefined),
110
+ deleteAllRefreshTokensForUser: jest.fn().mockResolvedValue(undefined),
111
+ listRefreshTokensForUser: jest.fn().mockResolvedValue([]),
112
+ deleteRefreshTokenById: jest.fn().mockResolvedValue(undefined),
113
+ createPasswordResetToken: jest.fn().mockResolvedValue(undefined),
114
+ findValidPasswordResetToken: jest.fn().mockResolvedValue(null),
115
+ markPasswordResetTokenUsed: jest.fn().mockResolvedValue(undefined),
116
+ deleteExpiredPasswordResetTokens: jest.fn().mockResolvedValue(undefined)
117
+ } as unknown as jest.Mocked<AuthRepository>;
118
+
119
+ // Password mocks
120
+ (validatePasswordStrength as jest.Mock).mockReturnValue({ valid: true,
121
+ errors: [] });
122
+ (hashPassword as jest.Mock).mockResolvedValue("hashed-pw");
123
+ (verifyPassword as jest.Mock).mockResolvedValue(true);
124
+
125
+ const config: AuthModuleConfig = {
126
+ authRepo: mockAuthRepo,
127
+ allowRegistration: true,
128
+ authHooks: opts.authHooks
129
+ };
130
+
131
+ const app = new Hono<HonoEnv>();
132
+ app.onError(errorHandler);
133
+ app.route("/auth", createAuthRoutes(config));
134
+ return app;
135
+ }
136
+
137
+ function json(body: Record<string, unknown>) {
138
+ return {
139
+ method: "POST" as const,
140
+ headers: { "Content-Type": "application/json" },
141
+ body: JSON.stringify(body)
142
+ };
143
+ }
144
+
145
+ // ═══════════════════════════════════════════════════════════════════════════
146
+ // TESTS
147
+ // ═══════════════════════════════════════════════════════════════════════════
148
+
149
+ describe("transformAuthResponse hook", () => {
150
+ beforeAll(() => {
151
+ configureJwt({ secret: TEST_SECRET,
152
+ accessExpiresIn: "1h" });
153
+ });
154
+
155
+ beforeEach(() => {
156
+ jest.clearAllMocks();
157
+ });
158
+
159
+ // ── Registration ────────────────────────────────────────────────────
160
+
161
+ describe("POST /auth/register", () => {
162
+ it("calls transformAuthResponse with the response and 'register' method", async () => {
163
+ const transformHook = jest.fn(async (response: AuthResponsePayload) => ({
164
+ ...response,
165
+ tokens: { ...response.tokens, firebaseToken: "custom-firebase-token" }
166
+ }));
167
+ const app = createApp({ authHooks: { transformAuthResponse: transformHook } });
168
+
169
+ const res = await app.request("/auth/register", json({ email: "new@test.com", password: "StrongPass1" }));
170
+ expect(res.status).toBe(201);
171
+ const body = await res.json() as Record<string, unknown>;
172
+ const tokens = body.tokens as Record<string, unknown>;
173
+
174
+ // Verify the hook was called
175
+ expect(transformHook).toHaveBeenCalledTimes(1);
176
+
177
+ // Verify context
178
+ const context = transformHook.mock.calls[0][1] as TransformAuthResponseContext;
179
+ expect(context.method).toBe("register");
180
+ expect(context.userId).toBeTruthy();
181
+ expect(context.request).toBeInstanceOf(Request);
182
+
183
+ // Verify the custom field was injected
184
+ expect(tokens.firebaseToken).toBe("custom-firebase-token");
185
+ // Original fields still present
186
+ expect(tokens.accessToken).toBeTruthy();
187
+ expect(tokens.refreshToken).toBeTruthy();
188
+ });
189
+ });
190
+
191
+ // ── Login ────────────────────────────────────────────────────────────
192
+
193
+ describe("POST /auth/login", () => {
194
+ it("calls transformAuthResponse with 'login' method", async () => {
195
+ const transformHook = jest.fn(async (response: AuthResponsePayload) => ({
196
+ ...response,
197
+ tokens: { ...response.tokens, externalToken: "ext-123" }
198
+ }));
199
+ const app = createApp({ authHooks: { transformAuthResponse: transformHook } });
200
+
201
+ // Setup: user exists
202
+ mockAuthRepo.getUserByEmail.mockResolvedValueOnce(mockUser());
203
+
204
+ const res = await app.request("/auth/login", json({ email: "test@example.com", password: "StrongPass1" }));
205
+ expect(res.status).toBe(200);
206
+ const body = await res.json() as Record<string, unknown>;
207
+ const tokens = body.tokens as Record<string, unknown>;
208
+
209
+ expect(transformHook).toHaveBeenCalledTimes(1);
210
+ const context = transformHook.mock.calls[0][1] as TransformAuthResponseContext;
211
+ expect(context.method).toBe("login");
212
+ expect(tokens.externalToken).toBe("ext-123");
213
+ });
214
+ });
215
+
216
+ // ── Refresh ──────────────────────────────────────────────────────────
217
+
218
+ describe("POST /auth/refresh", () => {
219
+ it("calls transformAuthResponse with 'refresh' method (tokens-only response)", async () => {
220
+ const transformHook = jest.fn(async (response: AuthResponsePayload) => ({
221
+ ...response,
222
+ tokens: { ...response.tokens, refreshedExternalToken: "refreshed-ext" }
223
+ }));
224
+ const app = createApp({ authHooks: { transformAuthResponse: transformHook } });
225
+
226
+ // Setup: valid refresh token exists
227
+ const rawRefreshToken = "a".repeat(80);
228
+ const hashedToken = hashRefreshToken(rawRefreshToken);
229
+ mockAuthRepo.findRefreshTokenByHash.mockResolvedValueOnce({
230
+ id: "rt-1",
231
+ userId: "user-1",
232
+ tokenHash: hashedToken,
233
+ expiresAt: new Date(Date.now() + 86400000), // 1 day from now
234
+ createdAt: new Date(),
235
+ userAgent: "test",
236
+ ipAddress: "127.0.0.1"
237
+ });
238
+ mockAuthRepo.getUserRoleIds.mockResolvedValueOnce(["editor"]);
239
+
240
+ const res = await app.request("/auth/refresh", json({ refreshToken: rawRefreshToken }));
241
+ expect(res.status).toBe(200);
242
+ const body = await res.json() as Record<string, unknown>;
243
+ const tokens = body.tokens as Record<string, unknown>;
244
+
245
+ expect(transformHook).toHaveBeenCalledTimes(1);
246
+
247
+ // Verify refresh context
248
+ const hookResponse = transformHook.mock.calls[0][0] as AuthResponsePayload;
249
+ const context = transformHook.mock.calls[0][1] as TransformAuthResponseContext;
250
+ expect(context.method).toBe("refresh");
251
+ expect(hookResponse.user).toBeUndefined(); // refresh has no user
252
+ expect(tokens.refreshedExternalToken).toBe("refreshed-ext");
253
+ });
254
+ });
255
+
256
+ // ── Error handling ───────────────────────────────────────────────────
257
+
258
+ describe("error handling", () => {
259
+ it("returns untransformed response when hook throws", async () => {
260
+ const transformHook = jest.fn(async () => {
261
+ throw new Error("External service down");
262
+ });
263
+ const app = createApp({ authHooks: { transformAuthResponse: transformHook } });
264
+
265
+ const res = await app.request("/auth/register", json({ email: "new@test.com", password: "StrongPass1" }));
266
+ expect(res.status).toBe(201);
267
+ const body = await res.json() as Record<string, unknown>;
268
+ const tokens = body.tokens as Record<string, unknown>;
269
+ const user = body.user as Record<string, unknown>;
270
+
271
+ // Response is still valid (untransformed fallback)
272
+ expect(tokens.accessToken).toBeTruthy();
273
+ expect(tokens.refreshToken).toBeTruthy();
274
+ expect(user.email).toBe("new@test.com");
275
+
276
+ // Error was logged
277
+ expect(logger.error).toHaveBeenCalledWith(
278
+ "[AuthHooks] transformAuthResponse error",
279
+ expect.objectContaining({ error: "External service down" })
280
+ );
281
+ });
282
+ });
283
+
284
+ // ── Opt-in behavior ──────────────────────────────────────────────────
285
+
286
+ describe("opt-in behavior", () => {
287
+ it("does not alter response when hook is not provided", async () => {
288
+ const app = createApp(); // no hooks
289
+
290
+ const res = await app.request("/auth/register", json({ email: "new@test.com", password: "StrongPass1" }));
291
+ expect(res.status).toBe(201);
292
+ const body = await res.json() as Record<string, unknown>;
293
+ const tokens = body.tokens as Record<string, unknown>;
294
+ const user = body.user as Record<string, unknown>;
295
+
296
+ // Standard response, no extra fields
297
+ expect(tokens.accessToken).toBeTruthy();
298
+ expect(tokens.refreshToken).toBeTruthy();
299
+ expect(user.email).toBe("new@test.com");
300
+ expect(Object.keys(tokens as object)).toEqual(
301
+ expect.arrayContaining(["accessToken", "refreshToken", "accessTokenExpiresAt"])
302
+ );
303
+ });
304
+ });
305
+ });
package/build-errors.txt DELETED
@@ -1,52 +0,0 @@
1
-
2
- > @rebasepro/server-core@0.0.1 build /Users/francesco/rebase/packages/server-core
3
- > vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json
4
-
5
- vite v5.4.21 building for production...
6
- transforming...
7
- ✓ 1641 modules transformed.
8
- rendering chunks...
9
- computing gzip size...
10
- dist/index.es.js 1.68 kB │ gzip: 0.80 kB │ map: 0.09 kB
11
- dist/index-CvlusawO.js 8.86 kB │ gzip: 2.11 kB │ map: 16.79 kB
12
- dist/index-BGGPU0RK.js 1,946.14 kB │ gzip: 411.28 kB │ map: 4,292.57 kB
13
- dist/index.umd.js 2,070.48 kB │ gzip: 419.31 kB │ map: 4,326.51 kB
14
- ✓ built in 2.97s
15
- src/api/graphql/graphql-schema-generator.ts(65,34): error TS18046: 'source' is of type 'unknown'.
16
- src/api/graphql/graphql-schema-generator.ts(72,51): error TS18046: 'source' is of type 'unknown'.
17
- src/api/graphql/graphql-schema-generator.ts(136,13): error TS2322: Type 'Record<string, unknown>' is not assignable to type 'ThunkObjMap<GraphQLInputFieldConfig>'.
18
- src/api/graphql/graphql-schema-generator.ts(178,17): error TS2322: Type '(_: unknown, args: any, context: { driver: DataDriver; }) => Promise<Entity<Record<string, unknown>> | undefined>' is not assignable to type 'GraphQLFieldResolver<unknown, unknown, any, unknown>'.
19
- Types of parameters 'context' and 'context' are incompatible.
20
- Type 'unknown' is not assignable to type '{ driver: DataDriver; }'.
21
- src/api/graphql/graphql-schema-generator.ts(198,17): error TS2322: Type '(_: unknown, args: any, context: { driver: DataDriver; }) => Promise<Entity<Record<string, unknown>>[]>' is not assignable to type 'GraphQLFieldResolver<unknown, unknown, any, unknown>'.
22
- Types of parameters 'context' and 'context' are incompatible.
23
- Type 'unknown' is not assignable to type '{ driver: DataDriver; }'.
24
- src/api/graphql/graphql-schema-generator.ts(249,17): error TS2322: Type '(_: unknown, args: any, context: { driver: DataDriver; }) => Promise<Entity<Record<string, unknown>>>' is not assignable to type 'GraphQLFieldResolver<unknown, unknown, any, unknown>'.
25
- Types of parameters 'context' and 'context' are incompatible.
26
- Type 'unknown' is not assignable to type '{ driver: DataDriver; }'.
27
- src/api/graphql/graphql-schema-generator.ts(269,17): error TS2322: Type '(_: unknown, args: any, context: { driver: DataDriver; }) => Promise<Entity<Record<string, unknown>>>' is not assignable to type 'GraphQLFieldResolver<unknown, unknown, any, unknown>'.
28
- Types of parameters 'context' and 'context' are incompatible.
29
- Type 'unknown' is not assignable to type '{ driver: DataDriver; }'.
30
- src/api/graphql/graphql-schema-generator.ts(288,17): error TS2322: Type '(_: unknown, args: any, context: { driver: DataDriver; }) => Promise<boolean>' is not assignable to type 'GraphQLFieldResolver<unknown, unknown, any, unknown>'.
31
- Types of parameters 'context' and 'context' are incompatible.
32
- Type 'unknown' is not assignable to type '{ driver: DataDriver; }'.
33
- src/api/rest/api-generator.ts(90,63): error TS18046: 'entities' is of type 'unknown'.
34
- src/collections/BackendCollectionRegistry.ts(18,9): error TS2322: Type '{}[]' is not assignable to type 'string[]'.
35
- Type '{}' is not assignable to type 'string'.
36
- src/functions/function-loader.ts(10,15): error TS2344: Type 'unknown' does not satisfy the constraint 'Env'.
37
- src/functions/function-routes.ts(27,37): error TS2345: Argument of type 'Hono<unknown, BlankSchema, "/">' is not assignable to parameter of type 'Hono<Env, BlankSchema, "/", "/">'.
38
- The types of 'get(...).get' are incompatible between these types.
39
- Type 'HandlerInterface<unknown, "get", BlankSchema, "/", any>' is not assignable to type 'HandlerInterface<Env, "get", BlankSchema, "/", any>'.
40
- Types of parameters 'handlers' and 'handlers' are incompatible.
41
- Type 'H<Env, any, any, any>' is not assignable to type 'H<unknown, any, any, any>'.
42
- Type 'Handler<Env, any, any, any>' is not assignable to type 'H<unknown, any, any, any>'.
43
- Type 'Handler<Env, any, any, any>' is not assignable to type 'Handler<unknown, any, any, any>'.
44
- Types of parameters 'c' and 'c' are incompatible.
45
- Type 'Context<unknown, any, any>' is not assignable to type 'Context<Env, any, any>'.
46
- Types of property 'env' are incompatible.
47
- Type 'unknown' is not assignable to type 'object | undefined'.
48
- src/history/history-routes.ts(48,30): error TS18046: 'historyService' is of type 'unknown'.
49
- src/history/history-routes.ts(87,36): error TS18046: 'historyService' is of type 'unknown'.
50
- src/serve-spa.ts(38,36): error TS2344: Type 'unknown' does not satisfy the constraint 'Env'.
51
- src/storage/LocalStorageController.ts(346,33): error TS2339: Property 'code' does not exist on type '{}'.
52
-  ELIFECYCLE  Command failed with exit code 2.
@@ -1,21 +0,0 @@
1
- export declare class AstSchemaEditor {
2
- private project;
3
- private collectionsDir;
4
- constructor(collectionsDir: string);
5
- /**
6
- * Sanitize collectionId to prevent path traversal attacks.
7
- * Only allows alphanumeric characters, underscores, and hyphens.
8
- */
9
- private sanitizeCollectionId;
10
- /**
11
- * Resolve a file path and ensure it falls within the collectionsDir.
12
- */
13
- private safePath;
14
- private getCollectionFile;
15
- private getCollectionObject;
16
- private convertJsonToAstString;
17
- saveProperty(collectionId: string, propertyKey: string, propertyConfig: Record<string, unknown>): Promise<void>;
18
- deleteProperty(collectionId: string, propertyKey: string): Promise<void>;
19
- saveCollection(collectionId: string, collectionData: Record<string, unknown>): Promise<void>;
20
- deleteCollection(collectionId: string): Promise<void>;
21
- }
@@ -1,2 +0,0 @@
1
- import { PostgresCollection } from "@rebasepro/types";
2
- export declare const callbacksTestCollection: PostgresCollection;
@@ -1,53 +0,0 @@
1
- import type { ErrorHandler } from "hono";
2
- import type { HonoEnv } from "./types";
3
- /**
4
- * Standardized API error class.
5
- * Throw this from any route handler — the errorHandler middleware
6
- * will format it into `{ error: { message, code, details? } }`.
7
- */
8
- export declare class ApiError extends Error {
9
- readonly statusCode: number;
10
- readonly code: string;
11
- readonly details?: unknown;
12
- constructor(statusCode: number, code: string, message: string, details?: unknown);
13
- static badRequest(message: string, code?: string, details?: unknown): ApiError;
14
- static unauthorized(message: string, code?: string): ApiError;
15
- static forbidden(message: string, code?: string): ApiError;
16
- static notFound(message: string, code?: string): ApiError;
17
- static conflict(message: string, code?: string): ApiError;
18
- static internal(message: string, code?: string): ApiError;
19
- static serviceUnavailable(message: string, code?: string): ApiError;
20
- }
21
- /**
22
- * Canonical error response shape:
23
- * `{ error: { message: string, code: string, details?: unknown } }`
24
- */
25
- export interface ErrorResponse {
26
- error: {
27
- message: string;
28
- code: string;
29
- details?: unknown;
30
- /** Request correlation ID for tracing (echoes X-Request-ID). */
31
- requestId?: string;
32
- };
33
- }
34
- /**
35
- * General shape of errors that flow through the API error handler.
36
- * Extends Error with optional HTTP status, error code, and details.
37
- */
38
- export interface RebaseApiError extends Error {
39
- statusCode?: number;
40
- code?: string;
41
- details?: unknown;
42
- }
43
- /**
44
- * Type guard for errors that carry optional API metadata (statusCode, code, details).
45
- * Returns true for any Error instance — the optional properties are then
46
- * checked via normal property access.
47
- */
48
- export declare function isRebaseApiError(error: unknown): error is RebaseApiError;
49
- /**
50
- * Hono error-handling middleware (`app.onError`).
51
- * Converts any error into the canonical `{ error: { message, code } }` shape.
52
- */
53
- export declare const errorHandler: ErrorHandler<HonoEnv>;
@@ -1,35 +0,0 @@
1
- import { GraphQLSchema } from "graphql";
2
- import { DataDriver, EntityCollection } from "@rebasepro/types";
3
- /**
4
- * Lightweight GraphQL schema generator that leverages existing DataDriver
5
- * No duplication - uses your existing data layer and services
6
- */
7
- export declare class GraphQLSchemaGenerator {
8
- private collections;
9
- private driver;
10
- private typeRegistry;
11
- private inputTypeRegistry;
12
- constructor(collections: EntityCollection[], driver: DataDriver);
13
- /**
14
- * Generate complete GraphQL schema using existing DataDriver
15
- */
16
- generateSchema(): GraphQLSchema;
17
- /**
18
- * Create GraphQL type for an entity collection
19
- */
20
- private createEntityType;
21
- private convertPropertyToField;
22
- private createInputType;
23
- private convertPropertyToInputType;
24
- /**
25
- * Create Query type using existing DataDriver methods
26
- */
27
- private createQueryType;
28
- /**
29
- * Create Mutation type using existing DataDriver methods
30
- */
31
- private createMutationType;
32
- private getTypeName;
33
- private getSingleQueryName;
34
- private getListQueryName;
35
- }
@@ -1 +0,0 @@
1
- export * from "./graphql-schema-generator";
@@ -1,9 +0,0 @@
1
- /**
2
- * API generation infrastructure for Rebase
3
- * Automatically generates GraphQL and REST APIs from EntityCollection definitions
4
- */
5
- export * from "./graphql";
6
- export * from "./rest";
7
- export * from "./types";
8
- export * from "./errors";
9
- export * from "./server";
@@ -1,37 +0,0 @@
1
- import { Hono } from "hono";
2
- import type { MiddlewareHandler } from "hono";
3
- import type { HonoEnv } from "./types";
4
- export interface LogEntry {
5
- id: string;
6
- timestamp: string;
7
- level: "debug" | "info" | "warn" | "error";
8
- source: "api" | "auth" | "storage" | "realtime" | "system";
9
- message: string;
10
- metadata?: Record<string, unknown>;
11
- }
12
- declare class LogRingBuffer {
13
- private buffer;
14
- private maxSize;
15
- private idCounter;
16
- constructor(maxSize?: number);
17
- push(entry: Omit<LogEntry, "id">): void;
18
- query(options: {
19
- level?: string;
20
- source?: string;
21
- search?: string;
22
- limit?: number;
23
- offset?: number;
24
- since?: string;
25
- }): {
26
- entries: LogEntry[];
27
- total: number;
28
- };
29
- getLatest(count?: number): LogEntry[];
30
- }
31
- export declare const logBuffer: LogRingBuffer;
32
- /** Add a log entry */
33
- export declare function addLog(level: LogEntry["level"], source: LogEntry["source"], message: string, metadata?: Record<string, unknown>): void;
34
- /** Hono middleware to log API requests */
35
- export declare function logMiddleware(): MiddlewareHandler<HonoEnv>;
36
- declare const app: Hono<HonoEnv, import("hono/types").BlankSchema, "/">;
37
- export default app;
@@ -1,16 +0,0 @@
1
- import { EntityCollection } from "@rebasepro/types";
2
- /**
3
- * OpenAPI 3.0.3 specification generator.
4
- *
5
- * Produces a spec that exactly mirrors the REST API consumed by the
6
- * Rebase SDK client (`@rebasepro/client`).
7
- *
8
- * Routes are mounted at `{basePath}/data/{slug}` by `initializeRebaseBackend`.
9
- */
10
- export interface OpenApiGeneratorOptions {
11
- /** Base path for the API (e.g. "/api"). Defaults to "/api". */
12
- basePath?: string;
13
- /** Whether auth is enabled on data routes. Defaults to true. */
14
- requireAuth?: boolean;
15
- }
16
- export declare function generateOpenApiSpec(collections: EntityCollection[], options?: OpenApiGeneratorOptions): Record<string, unknown>;
@@ -1,88 +0,0 @@
1
- import { Hono } from "hono";
2
- import { AuthAdapter, DataDriver, EntityCollection, DataHooks } from "@rebasepro/types";
3
- import { HonoEnv } from "../types";
4
- /**
5
- * Lightweight REST API generator that leverages existing Rebase DataDriver.
6
- * Supports `include` query parameter for eager-loading relations via Drizzle.
7
- */
8
- export declare class RestApiGenerator {
9
- private collections;
10
- private router;
11
- private driver;
12
- private dataHooks?;
13
- private authAdapter?;
14
- constructor(collections: EntityCollection[], driver: DataDriver, dataHooks?: DataHooks, authAdapter?: AuthAdapter);
15
- /** Build a BackendHookContext from a Hono context */
16
- private buildHookContext;
17
- /**
18
- * Generate REST routes using existing DataDriver
19
- */
20
- generateRoutes(): Hono<HonoEnv>;
21
- /**
22
- * Check API key permissions for a collection operation.
23
- * Throws 403 if the key doesn't have the required permission.
24
- * No-ops if the request is not authenticated via an API key.
25
- */
26
- private enforceApiKeyPermission;
27
- /**
28
- * Get the request-scoped driver. Throws if none is set — never falls
29
- * back to the unscoped `this.driver` to avoid bypassing RLS/auth.
30
- */
31
- private getScopedDriver;
32
- /**
33
- * Get the typed RestFetchService from a driver if it exposes one (for include support).
34
- */
35
- private getFetchService;
36
- /**
37
- * Create REST routes for a collection using existing Rebase patterns
38
- */
39
- private createCollectionRoutes;
40
- /**
41
- * Catch-all routes for subcollection paths.
42
- *
43
- * Matches URL patterns like:
44
- * GET /authors/111094/posts → list child collection
45
- * GET /authors/111094/posts/43 → get child entity
46
- * POST /authors/111094/posts → create child entity
47
- * PUT /authors/111094/posts/43 → update child entity
48
- * DELETE /authors/111094/posts/43 → delete child entity
49
- *
50
- * The `:rest{.+}` regex param captures the full remainder of the URL
51
- * path (Hono v4 `*` wildcard does not populate `c.req.param("*")`).
52
- * We split it into segments and reconstruct the `collectionPath`
53
- * (e.g. "authors/111094/posts") and optional `entityId` (e.g. "43").
54
- *
55
- * The DataDriver.saveEntity / fetchCollection / etc. already know how to
56
- * resolve multi-segment relation paths, so we just forward to them.
57
- */
58
- private createSubcollectionRoutes;
59
- /**
60
- * Format successful API response - flattened for traditional REST API
61
- */
62
- private formatResponse;
63
- /**
64
- * Flatten Rebase entity structure to traditional REST format
65
- */
66
- private flattenEntity;
67
- /**
68
- * Fetch raw collection data without Entity wrapper (fallback for non-Postgres)
69
- */
70
- private fetchRawCollection;
71
- /**
72
- * Count raw entities for a collection
73
- */
74
- private countRawEntities;
75
- /**
76
- * Fetch single entity raw data without Entity wrapper (fallback)
77
- */
78
- private fetchRawEntity;
79
- /**
80
- * Apply data.afterRead hook to a single entity.
81
- * Returns the transformed entity, or null to filter it out.
82
- */
83
- private applyAfterRead;
84
- /**
85
- * Apply data.afterRead hook to an array of entities, filtering out nulls.
86
- */
87
- private applyAfterReadBatch;
88
- }
@@ -1 +0,0 @@
1
- export * from "./api-generator";
@@ -1,11 +0,0 @@
1
- import type { LogicalCondition, FilterCondition } from "@rebasepro/types";
2
- import { QueryOptions } from "../types";
3
- /**
4
- * Map PostgREST-style operators to Rebase WhereFilterOp
5
- */
6
- export declare function mapOperator(op: string): string | null;
7
- export declare function parseLogicalString(str: string): FilterCondition | LogicalCondition;
8
- /**
9
- * Parse query parameters into QueryOptions
10
- */
11
- export declare function parseQueryOptions(query: Record<string, unknown>): QueryOptions;
@@ -1,3 +0,0 @@
1
- import { Hono } from "hono";
2
- import { HonoEnv } from "./types";
3
- export declare function createSchemaEditorRoutes(collectionsDir: string): Hono<HonoEnv>;