@rebasepro/server-core 0.6.0 → 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.
- package/package.json +14 -14
- package/src/api/errors.ts +16 -2
- package/src/api/graphql/graphql-schema-generator.ts +45 -10
- package/src/api/rest/api-generator.ts +19 -2
- package/src/api/server.ts +10 -2
- package/src/api/types.ts +6 -0
- package/src/auth/adapter-middleware.ts +10 -2
- package/src/auth/admin-roles-route.ts +36 -0
- package/src/auth/admin-users-route.ts +302 -0
- package/src/auth/api-keys/api-key-middleware.ts +4 -3
- package/src/auth/api-keys/api-key-routes.ts +12 -2
- package/src/auth/api-keys/api-key-store.ts +83 -66
- package/src/auth/api-keys/api-key-types.ts +8 -0
- package/src/auth/apple-oauth.ts +2 -1
- package/src/auth/auth-hooks.ts +21 -0
- package/src/auth/bitbucket-oauth.ts +2 -1
- package/src/auth/builtin-auth-adapter.ts +28 -4
- package/src/auth/custom-auth-adapter.ts +2 -0
- package/src/auth/discord-oauth.ts +2 -1
- package/src/auth/facebook-oauth.ts +2 -1
- package/src/auth/github-oauth.ts +2 -1
- package/src/auth/gitlab-oauth.ts +2 -1
- package/src/auth/google-oauth.ts +8 -4
- package/src/auth/index.ts +2 -0
- package/src/auth/interfaces.ts +27 -0
- package/src/auth/linkedin-oauth.ts +2 -1
- package/src/auth/magic-link-routes.ts +167 -0
- package/src/auth/mfa-crypto.ts +91 -0
- package/src/auth/mfa-routes.ts +34 -10
- package/src/auth/microsoft-oauth.ts +2 -1
- package/src/auth/middleware.ts +10 -1
- package/src/auth/reset-password-admin.ts +17 -1
- package/src/auth/routes.ts +77 -8
- package/src/auth/session-routes.ts +15 -3
- package/src/auth/slack-oauth.ts +2 -1
- package/src/auth/spotify-oauth.ts +2 -1
- package/src/auth/twitter-oauth.ts +8 -1
- package/src/cron/cron-store.ts +25 -23
- package/src/email/index.ts +3 -2
- package/src/email/templates.ts +82 -0
- package/src/email/types.ts +16 -0
- package/src/init.ts +137 -5
- package/src/services/routed-realtime-service.ts +113 -0
- package/src/storage/routes.ts +4 -0
- package/src/utils/dev-port.ts +13 -7
- package/test/auth-routes.test.ts +54 -4
- package/test/custom-auth-adapter.test.ts +20 -1
- package/test/env.test.ts +9 -19
- package/test/multi-datasource-routing.test.ts +113 -0
- package/test/routed-realtime-service.test.ts +86 -0
- package/test/transform-auth-response.test.ts +305 -0
- package/vite.config.ts +5 -0
- package/build-errors.txt +0 -52
- package/dist/api/ast-schema-editor.d.ts +0 -21
- package/dist/api/collections_for_test/callbacks_test_collection.d.ts +0 -2
- package/dist/api/errors.d.ts +0 -53
- package/dist/api/graphql/graphql-schema-generator.d.ts +0 -35
- package/dist/api/graphql/index.d.ts +0 -1
- package/dist/api/index.d.ts +0 -9
- package/dist/api/logs-routes.d.ts +0 -37
- package/dist/api/openapi-generator.d.ts +0 -16
- package/dist/api/rest/api-generator.d.ts +0 -88
- package/dist/api/rest/index.d.ts +0 -1
- package/dist/api/rest/query-parser.d.ts +0 -11
- package/dist/api/schema-editor-routes.d.ts +0 -3
- package/dist/api/server.d.ts +0 -40
- package/dist/api/types.d.ts +0 -98
- package/dist/auth/adapter-middleware.d.ts +0 -37
- package/dist/auth/admin-user-ops.d.ts +0 -79
- package/dist/auth/api-keys/api-key-middleware.d.ts +0 -39
- package/dist/auth/api-keys/api-key-permission-guard.d.ts +0 -32
- package/dist/auth/api-keys/api-key-routes.d.ts +0 -20
- package/dist/auth/api-keys/api-key-store.d.ts +0 -35
- package/dist/auth/api-keys/api-key-types.d.ts +0 -88
- package/dist/auth/api-keys/index.d.ts +0 -17
- package/dist/auth/apple-oauth.d.ts +0 -30
- package/dist/auth/auth-hooks.d.ts +0 -225
- package/dist/auth/bitbucket-oauth.d.ts +0 -11
- package/dist/auth/builtin-auth-adapter.d.ts +0 -49
- package/dist/auth/crypto-utils.d.ts +0 -16
- package/dist/auth/custom-auth-adapter.d.ts +0 -39
- package/dist/auth/discord-oauth.d.ts +0 -14
- package/dist/auth/facebook-oauth.d.ts +0 -14
- package/dist/auth/github-oauth.d.ts +0 -15
- package/dist/auth/gitlab-oauth.d.ts +0 -13
- package/dist/auth/google-oauth.d.ts +0 -47
- package/dist/auth/index.d.ts +0 -36
- package/dist/auth/interfaces.d.ts +0 -401
- package/dist/auth/jwt.d.ts +0 -55
- package/dist/auth/linkedin-oauth.d.ts +0 -18
- package/dist/auth/mfa-routes.d.ts +0 -6
- package/dist/auth/mfa.d.ts +0 -49
- package/dist/auth/microsoft-oauth.d.ts +0 -16
- package/dist/auth/middleware.d.ts +0 -106
- package/dist/auth/password.d.ts +0 -22
- package/dist/auth/rate-limiter.d.ts +0 -50
- package/dist/auth/reset-password-admin.d.ts +0 -29
- package/dist/auth/rls-scope.d.ts +0 -31
- package/dist/auth/routes.d.ts +0 -33
- package/dist/auth/session-routes.d.ts +0 -25
- package/dist/auth/slack-oauth.d.ts +0 -12
- package/dist/auth/spotify-oauth.d.ts +0 -12
- package/dist/auth/twitter-oauth.d.ts +0 -18
- package/dist/backend-CIxN4FVm.js +0 -15
- package/dist/backend-CIxN4FVm.js.map +0 -1
- package/dist/chunk-Dze3rakg.js +0 -42
- package/dist/collections/BackendCollectionRegistry.d.ts +0 -13
- package/dist/collections/loader.d.ts +0 -5
- package/dist/cron/cron-loader.d.ts +0 -17
- package/dist/cron/cron-routes.d.ts +0 -14
- package/dist/cron/cron-scheduler.d.ts +0 -106
- package/dist/cron/cron-store.d.ts +0 -32
- package/dist/cron/index.d.ts +0 -6
- package/dist/db/interfaces.d.ts +0 -18
- package/dist/dist-CZKP-Xz4.js +0 -832
- package/dist/dist-CZKP-Xz4.js.map +0 -1
- package/dist/email/index.d.ts +0 -6
- package/dist/email/smtp-email-service.d.ts +0 -25
- package/dist/email/templates.d.ts +0 -42
- package/dist/email/types.d.ts +0 -108
- package/dist/env.d.ts +0 -102
- package/dist/from-VbwD7xRf.js +0 -3849
- package/dist/from-VbwD7xRf.js.map +0 -1
- package/dist/functions/function-loader.d.ts +0 -17
- package/dist/functions/function-routes.d.ts +0 -10
- package/dist/functions/index.d.ts +0 -3
- package/dist/history/history-routes.d.ts +0 -23
- package/dist/history/index.d.ts +0 -1
- package/dist/index.d.ts +0 -33
- package/dist/index.es.js +0 -61726
- package/dist/index.es.js.map +0 -1
- package/dist/index.umd.js +0 -73216
- package/dist/index.umd.js.map +0 -1
- package/dist/init/docs.d.ts +0 -4
- package/dist/init/health.d.ts +0 -2
- package/dist/init/middlewares.d.ts +0 -10
- package/dist/init/shutdown.d.ts +0 -11
- package/dist/init/storage.d.ts +0 -5
- package/dist/init.d.ts +0 -258
- package/dist/jwt-DHcQRGC3.js +0 -4168
- package/dist/jwt-DHcQRGC3.js.map +0 -1
- package/dist/logger-BYU66ENZ.js +0 -94
- package/dist/logger-BYU66ENZ.js.map +0 -1
- package/dist/ms-BeBSuOXN.js +0 -125
- package/dist/ms-BeBSuOXN.js.map +0 -1
- package/dist/multipart-parser-CedBDOeC.js +0 -299
- package/dist/multipart-parser-CedBDOeC.js.map +0 -1
- package/dist/serve-spa.d.ts +0 -30
- package/dist/services/driver-registry.d.ts +0 -78
- package/dist/services/webhook-service.d.ts +0 -29
- package/dist/singleton.d.ts +0 -35
- package/dist/src-COaj0G3P.js +0 -1182
- package/dist/src-COaj0G3P.js.map +0 -1
- package/dist/storage/LocalStorageController.d.ts +0 -46
- package/dist/storage/S3StorageController.d.ts +0 -36
- package/dist/storage/image-transform.d.ts +0 -50
- package/dist/storage/index.d.ts +0 -28
- package/dist/storage/routes.d.ts +0 -38
- package/dist/storage/storage-registry.d.ts +0 -78
- package/dist/storage/tus-handler.d.ts +0 -51
- package/dist/storage/types.d.ts +0 -103
- package/dist/types/index.d.ts +0 -11
- package/dist/utils/dev-port.d.ts +0 -35
- package/dist/utils/logger.d.ts +0 -31
- package/dist/utils/logging.d.ts +0 -9
- package/dist/utils/request-id.d.ts +0 -4
- package/dist/utils/request-logger.d.ts +0 -19
- 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/vite.config.ts
CHANGED
|
@@ -53,6 +53,11 @@ export default defineConfig(() => ({
|
|
|
53
53
|
sourcemap: true,
|
|
54
54
|
rollupOptions: {
|
|
55
55
|
external: isExternal,
|
|
56
|
+
onwarn(warning, warn) {
|
|
57
|
+
if (warning.code === "MISSING_GLOBAL_NAME") return;
|
|
58
|
+
if (warning.code === "INEFFECTIVE_DYNAMIC_IMPORT") return;
|
|
59
|
+
warn(warning);
|
|
60
|
+
},
|
|
56
61
|
output: {
|
|
57
62
|
banner: 'import { createRequire as __createRequire } from "module"; import process from "process"; const require = __createRequire(import.meta.url);',
|
|
58
63
|
globals: {
|
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
|
-
}
|
package/dist/api/errors.d.ts
DELETED
|
@@ -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";
|
package/dist/api/index.d.ts
DELETED
|
@@ -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
|
-
}
|
package/dist/api/rest/index.d.ts
DELETED
|
@@ -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;
|