@rebasepro/server-core 0.7.0 → 0.9.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/README.md +2 -2
- package/dist/api/ast-schema-editor.d.ts +21 -0
- package/dist/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
- package/dist/api/errors.d.ts +53 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/logs-routes.d.ts +37 -0
- package/dist/api/openapi-generator.d.ts +16 -0
- package/dist/api/rest/api-generator.d.ts +68 -0
- package/{src/api/rest/index.ts → dist/api/rest/index.d.ts} +0 -1
- package/dist/api/rest/query-parser.d.ts +6 -0
- package/dist/api/schema-editor-routes.d.ts +3 -0
- package/dist/api/types.d.ts +56 -0
- package/dist/auth/adapter-middleware.d.ts +43 -0
- package/dist/auth/admin-roles-route.d.ts +18 -0
- package/dist/auth/admin-user-ops.d.ts +79 -0
- package/dist/auth/admin-users-route.d.ts +28 -0
- package/dist/auth/api-keys/api-key-middleware.d.ts +39 -0
- package/{src/auth/api-keys/api-key-permission-guard.ts → dist/auth/api-keys/api-key-permission-guard.d.ts} +2 -34
- package/dist/auth/api-keys/api-key-routes.d.ts +20 -0
- package/dist/auth/api-keys/api-key-store.d.ts +35 -0
- package/{src/auth/api-keys/api-key-types.ts → dist/auth/api-keys/api-key-types.d.ts} +0 -6
- package/{src/auth/api-keys/index.ts → dist/auth/api-keys/index.d.ts} +2 -22
- package/dist/auth/apple-oauth.d.ts +30 -0
- package/{src/auth/auth-hooks.ts → dist/auth/auth-hooks.d.ts} +15 -73
- package/dist/auth/bitbucket-oauth.d.ts +11 -0
- package/dist/auth/builtin-auth-adapter.d.ts +55 -0
- package/dist/auth/cookie-utils.d.ts +32 -0
- package/{src/auth/crypto-utils.ts → dist/auth/crypto-utils.d.ts} +1 -16
- package/dist/auth/custom-auth-adapter.d.ts +39 -0
- package/dist/auth/discord-oauth.d.ts +14 -0
- package/dist/auth/facebook-oauth.d.ts +14 -0
- package/dist/auth/github-oauth.d.ts +15 -0
- package/dist/auth/gitlab-oauth.d.ts +13 -0
- package/dist/auth/google-oauth.d.ts +47 -0
- package/{src/auth/index.ts → dist/auth/index.d.ts} +4 -22
- package/{src/auth/interfaces.ts → dist/auth/interfaces.d.ts} +20 -89
- package/dist/auth/jwt.d.ts +67 -0
- package/dist/auth/linkedin-oauth.d.ts +18 -0
- package/dist/auth/magic-link-routes.d.ts +32 -0
- package/dist/auth/mfa-crypto.d.ts +23 -0
- package/dist/auth/mfa-routes.d.ts +7 -0
- package/dist/auth/mfa.d.ts +49 -0
- package/dist/auth/microsoft-oauth.d.ts +16 -0
- package/dist/auth/middleware.d.ts +158 -0
- package/dist/auth/password.d.ts +22 -0
- package/dist/auth/rate-limiter.d.ts +50 -0
- package/dist/auth/reset-password-admin.d.ts +29 -0
- package/dist/auth/rls-scope.d.ts +31 -0
- package/dist/auth/routes.d.ts +66 -0
- package/dist/auth/session-routes.d.ts +29 -0
- package/dist/auth/slack-oauth.d.ts +12 -0
- package/dist/auth/spotify-oauth.d.ts +12 -0
- package/dist/auth/twitter-oauth.d.ts +18 -0
- package/dist/collections/BackendCollectionRegistry.d.ts +13 -0
- package/dist/collections/loader.d.ts +5 -0
- package/dist/cron/cron-loader.d.ts +18 -0
- package/dist/cron/cron-routes.d.ts +14 -0
- package/dist/cron/cron-scheduler.d.ts +106 -0
- package/dist/cron/cron-store.d.ts +32 -0
- package/dist/cron/define-cron.d.ts +28 -0
- package/{src/cron/index.ts → dist/cron/index.d.ts} +1 -0
- package/dist/db/interfaces.d.ts +18 -0
- package/dist/dynamic-import-Dvh-K5fl.js +22 -0
- package/dist/dynamic-import-Dvh-K5fl.js.map +1 -0
- package/dist/email/index.d.ts +6 -0
- package/dist/email/smtp-email-service.d.ts +30 -0
- package/dist/email/templates.d.ts +50 -0
- package/{src/email/types.ts → dist/email/types.d.ts} +40 -36
- package/dist/env.d.ts +102 -0
- package/dist/functions/define-function.d.ts +55 -0
- package/dist/functions/function-loader.d.ts +18 -0
- package/dist/functions/function-routes.d.ts +10 -0
- package/{src/functions/index.ts → dist/functions/index.d.ts} +2 -0
- package/dist/history/history-routes.d.ts +23 -0
- package/{src/history/index.ts → dist/history/index.d.ts} +0 -1
- package/dist/index.d.ts +35 -0
- package/dist/index.es.js +18184 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +48629 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/init/docs.d.ts +4 -0
- package/dist/init/health.d.ts +2 -0
- package/dist/init/middlewares.d.ts +10 -0
- package/dist/init/shutdown.d.ts +65 -0
- package/dist/init/storage.d.ts +5 -0
- package/dist/init.d.ts +344 -0
- package/dist/jwt-BwIn8xmk.js +4200 -0
- package/dist/jwt-BwIn8xmk.js.map +1 -0
- package/dist/logger-BYU66ENZ.js +94 -0
- package/dist/logger-BYU66ENZ.js.map +1 -0
- package/dist/ms-DnYXB-Wd.js +162 -0
- package/dist/ms-DnYXB-Wd.js.map +1 -0
- package/dist/openapi-generator-Z9oYWLf_.js +586 -0
- package/dist/openapi-generator-Z9oYWLf_.js.map +1 -0
- package/dist/schema-editor-routes-D3ef8zu1.js +219 -0
- package/dist/schema-editor-routes-D3ef8zu1.js.map +1 -0
- package/dist/serve-spa.d.ts +36 -0
- package/dist/services/driver-registry.d.ts +78 -0
- package/dist/services/routed-realtime-service.d.ts +43 -0
- package/dist/services/webhook-service.d.ts +29 -0
- package/dist/singleton.d.ts +51 -0
- package/dist/src-B4OLmNVa.js +437 -0
- package/dist/src-B4OLmNVa.js.map +1 -0
- package/dist/src-B5WkP1Rv.js +24778 -0
- package/dist/src-B5WkP1Rv.js.map +1 -0
- package/dist/storage/GCSStorageController.d.ts +43 -0
- package/dist/storage/LocalStorageController.d.ts +46 -0
- package/dist/storage/S3StorageController.d.ts +40 -0
- package/dist/storage/image-transform.d.ts +50 -0
- package/{src/storage/index.ts → dist/storage/index.d.ts} +6 -21
- package/dist/storage/routes.d.ts +70 -0
- package/dist/storage/storage-registry.d.ts +78 -0
- package/dist/storage/tus-handler.d.ts +53 -0
- package/{src/storage/types.ts → dist/storage/types.d.ts} +32 -38
- package/dist/types/index.d.ts +11 -0
- package/dist/utils/dev-port.d.ts +45 -0
- package/dist/utils/dynamic-import.d.ts +25 -0
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logging.d.ts +9 -0
- package/dist/utils/request-id.d.ts +4 -0
- package/dist/utils/request-logger.d.ts +19 -0
- package/{src/utils/sql.ts → dist/utils/sql.d.ts} +5 -16
- package/package.json +49 -22
- package/coverage/clover.xml +0 -3739
- package/coverage/coverage-final.json +0 -31
- package/coverage/lcov-report/base.css +0 -224
- package/coverage/lcov-report/block-navigation.js +0 -87
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +0 -266
- package/coverage/lcov-report/prettify.css +0 -1
- package/coverage/lcov-report/prettify.js +0 -2
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +0 -210
- package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +0 -952
- package/coverage/lcov-report/src/api/errors.ts.html +0 -472
- package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +0 -1069
- package/coverage/lcov-report/src/api/graphql/index.html +0 -116
- package/coverage/lcov-report/src/api/index.html +0 -176
- package/coverage/lcov-report/src/api/openapi-generator.ts.html +0 -565
- package/coverage/lcov-report/src/api/rest/api-generator.ts.html +0 -994
- package/coverage/lcov-report/src/api/rest/index.html +0 -131
- package/coverage/lcov-report/src/api/rest/query-parser.ts.html +0 -550
- package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +0 -202
- package/coverage/lcov-report/src/api/server.ts.html +0 -823
- package/coverage/lcov-report/src/auth/admin-routes.ts.html +0 -973
- package/coverage/lcov-report/src/auth/index.html +0 -176
- package/coverage/lcov-report/src/auth/jwt.ts.html +0 -574
- package/coverage/lcov-report/src/auth/middleware.ts.html +0 -745
- package/coverage/lcov-report/src/auth/password.ts.html +0 -310
- package/coverage/lcov-report/src/auth/services.ts.html +0 -2074
- package/coverage/lcov-report/src/collections/index.html +0 -116
- package/coverage/lcov-report/src/collections/loader.ts.html +0 -232
- package/coverage/lcov-report/src/db/auth-schema.ts.html +0 -523
- package/coverage/lcov-report/src/db/data-transformer.ts.html +0 -1753
- package/coverage/lcov-report/src/db/entityService.ts.html +0 -700
- package/coverage/lcov-report/src/db/index.html +0 -146
- package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +0 -4048
- package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +0 -883
- package/coverage/lcov-report/src/db/services/RelationService.ts.html +0 -3121
- package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +0 -442
- package/coverage/lcov-report/src/db/services/index.html +0 -176
- package/coverage/lcov-report/src/db/services/index.ts.html +0 -124
- package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +0 -1960
- package/coverage/lcov-report/src/index.html +0 -116
- package/coverage/lcov-report/src/services/driver-registry.ts.html +0 -631
- package/coverage/lcov-report/src/services/index.html +0 -131
- package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +0 -3025
- package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +0 -1189
- package/coverage/lcov-report/src/storage/S3StorageController.ts.html +0 -970
- package/coverage/lcov-report/src/storage/index.html +0 -161
- package/coverage/lcov-report/src/storage/storage-registry.ts.html +0 -646
- package/coverage/lcov-report/src/storage/types.ts.html +0 -451
- package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +0 -3082
- package/coverage/lcov-report/src/utils/index.html +0 -116
- package/coverage/lcov.info +0 -7179
- package/jest.config.cjs +0 -19
- package/src/api/ast-schema-editor.ts +0 -289
- package/src/api/collections_for_test/callbacks_test_collection.ts +0 -61
- package/src/api/errors.ts +0 -278
- package/src/api/graphql/graphql-schema-generator.ts +0 -383
- package/src/api/graphql/index.ts +0 -2
- package/src/api/index.ts +0 -11
- package/src/api/logs-routes.ts +0 -132
- package/src/api/openapi-generator.ts +0 -727
- package/src/api/rest/api-generator-count.test.ts +0 -126
- package/src/api/rest/api-generator.ts +0 -747
- package/src/api/rest/query-parser.ts +0 -300
- package/src/api/schema-editor-routes.ts +0 -41
- package/src/api/server.ts +0 -271
- package/src/api/types.ts +0 -104
- package/src/auth/adapter-middleware.ts +0 -114
- package/src/auth/admin-roles-route.ts +0 -36
- package/src/auth/admin-user-ops.ts +0 -236
- package/src/auth/admin-users-route.ts +0 -302
- package/src/auth/api-keys/api-key-middleware.ts +0 -133
- package/src/auth/api-keys/api-key-routes.ts +0 -193
- package/src/auth/api-keys/api-key-store.ts +0 -340
- package/src/auth/apple-oauth.ts +0 -122
- package/src/auth/bitbucket-oauth.ts +0 -84
- package/src/auth/builtin-auth-adapter.ts +0 -380
- package/src/auth/custom-auth-adapter.ts +0 -86
- package/src/auth/discord-oauth.ts +0 -85
- package/src/auth/facebook-oauth.ts +0 -74
- package/src/auth/github-oauth.ts +0 -112
- package/src/auth/gitlab-oauth.ts +0 -72
- package/src/auth/google-oauth.ts +0 -223
- package/src/auth/jwt.ts +0 -208
- package/src/auth/linkedin-oauth.ts +0 -83
- package/src/auth/magic-link-routes.ts +0 -167
- package/src/auth/mfa-crypto.ts +0 -91
- package/src/auth/mfa-routes.ts +0 -323
- package/src/auth/mfa.ts +0 -161
- package/src/auth/microsoft-oauth.ts +0 -90
- package/src/auth/middleware.ts +0 -369
- package/src/auth/password.ts +0 -77
- package/src/auth/rate-limiter.ts +0 -225
- package/src/auth/reset-password-admin.ts +0 -160
- package/src/auth/rls-scope.ts +0 -58
- package/src/auth/routes.ts +0 -802
- package/src/auth/session-routes.ts +0 -353
- package/src/auth/slack-oauth.ts +0 -73
- package/src/auth/spotify-oauth.ts +0 -69
- package/src/auth/twitter-oauth.ts +0 -128
- package/src/collections/BackendCollectionRegistry.ts +0 -20
- package/src/collections/loader.ts +0 -50
- package/src/cron/cron-loader.ts +0 -84
- package/src/cron/cron-routes.test.ts +0 -265
- package/src/cron/cron-routes.ts +0 -85
- package/src/cron/cron-scheduler.test.ts +0 -559
- package/src/cron/cron-scheduler.ts +0 -581
- package/src/cron/cron-store.ts +0 -166
- package/src/db/interfaces.ts +0 -60
- package/src/email/index.ts +0 -19
- package/src/email/smtp-email-service.ts +0 -123
- package/src/email/templates.ts +0 -470
- package/src/env.ts +0 -227
- package/src/functions/function-loader.ts +0 -116
- package/src/functions/function-routes.ts +0 -31
- package/src/history/history-routes.ts +0 -129
- package/src/index.ts +0 -70
- package/src/init/docs.ts +0 -47
- package/src/init/health.ts +0 -37
- package/src/init/middlewares.ts +0 -61
- package/src/init/shutdown.ts +0 -56
- package/src/init/storage.ts +0 -57
- package/src/init.ts +0 -1041
- package/src/serve-spa.ts +0 -89
- package/src/services/driver-registry.ts +0 -183
- package/src/services/routed-realtime-service.ts +0 -113
- package/src/services/webhook-service.ts +0 -155
- package/src/singleton.ts +0 -70
- package/src/storage/LocalStorageController.ts +0 -389
- package/src/storage/S3StorageController.ts +0 -300
- package/src/storage/image-transform.ts +0 -218
- package/src/storage/routes.ts +0 -368
- package/src/storage/storage-registry.ts +0 -188
- package/src/storage/tus-handler.ts +0 -315
- package/src/types/index.ts +0 -27
- package/src/utils/dev-port.ts +0 -196
- package/src/utils/logger.ts +0 -143
- package/src/utils/logging.ts +0 -40
- package/src/utils/request-id.ts +0 -40
- package/src/utils/request-logger.ts +0 -72
- package/test/api-generator.test.ts +0 -588
- package/test/api-key-permission-guard.test.ts +0 -144
- package/test/ast-schema-editor.test.ts +0 -89
- package/test/auth-middleware-hono.test.ts +0 -556
- package/test/auth-routes.test.ts +0 -1098
- package/test/backend-hooks-data.test.ts +0 -477
- package/test/custom-auth-adapter.test.ts +0 -189
- package/test/driver-registry.test.ts +0 -282
- package/test/email-templates.test.ts +0 -174
- package/test/env.test.ts +0 -128
- package/test/error-propagation.test.ts +0 -226
- package/test/errors-hono.test.ts +0 -133
- package/test/errors.test.ts +0 -155
- package/test/function-loader.test.ts +0 -127
- package/test/graphql-schema-generator.test.ts +0 -554
- package/test/jwt-security.test.ts +0 -182
- package/test/jwt.test.ts +0 -326
- package/test/mfa.test.ts +0 -197
- package/test/middleware.test.ts +0 -305
- package/test/multi-datasource-routing.test.ts +0 -113
- package/test/password.test.ts +0 -165
- package/test/query-parser.test.ts +0 -233
- package/test/rate-limiter.test.ts +0 -102
- package/test/reset-password-admin.test.ts +0 -113
- package/test/routed-realtime-service.test.ts +0 -86
- package/test/safe-compare.test.ts +0 -66
- package/test/singleton.test.ts +0 -59
- package/test/smtp-email-service.test.ts +0 -169
- package/test/storage-local.test.ts +0 -271
- package/test/storage-registry.test.ts +0 -282
- package/test/storage-routes.test.ts +0 -222
- package/test/storage-s3.test.ts +0 -304
- package/test/transform-auth-response.test.ts +0 -305
- package/test/webhook-service.test.ts +0 -260
- package/tsconfig.json +0 -54
- package/tsconfig.prod.json +0 -23
- package/vite.config.ts +0 -86
|
@@ -1,383 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
GraphQLSchema,
|
|
3
|
-
GraphQLObjectType,
|
|
4
|
-
GraphQLString,
|
|
5
|
-
GraphQLInt,
|
|
6
|
-
GraphQLFloat,
|
|
7
|
-
GraphQLBoolean,
|
|
8
|
-
GraphQLList,
|
|
9
|
-
GraphQLNonNull,
|
|
10
|
-
GraphQLFieldConfig,
|
|
11
|
-
GraphQLInputObjectType,
|
|
12
|
-
GraphQLInputFieldConfig
|
|
13
|
-
} from "graphql";
|
|
14
|
-
import { DataDriver, EntityCollection, FetchCollectionProps, Property, Entity } from "@rebasepro/types";
|
|
15
|
-
import { isOperationAllowed, type ApiKeyOperation } from "../../auth/api-keys/api-key-permission-guard";
|
|
16
|
-
import type { ApiKeyMasked } from "../../auth/api-keys/api-key-types";
|
|
17
|
-
|
|
18
|
-
/** Context shape provided by @hono/graphql-server (Hono's Context object). */
|
|
19
|
-
type GraphQLResolverContext = {
|
|
20
|
-
get?: (key: string) => unknown;
|
|
21
|
-
driver?: DataDriver;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Lightweight GraphQL schema generator that leverages existing DataDriver
|
|
26
|
-
* No duplication - uses your existing data layer and services
|
|
27
|
-
*/
|
|
28
|
-
export class GraphQLSchemaGenerator {
|
|
29
|
-
private collections: EntityCollection[];
|
|
30
|
-
private driver: DataDriver;
|
|
31
|
-
private typeRegistry = new Map<string, GraphQLObjectType>();
|
|
32
|
-
private inputTypeRegistry = new Map<string, GraphQLInputObjectType>();
|
|
33
|
-
|
|
34
|
-
constructor(collections: EntityCollection[], driver: DataDriver) {
|
|
35
|
-
this.collections = collections;
|
|
36
|
-
this.driver = driver;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Enforce API key permission scoping for a GraphQL resolver.
|
|
41
|
-
*
|
|
42
|
-
* Extracts the `apiKey` from the Hono context passed by `@hono/graphql-server`.
|
|
43
|
-
* If the request was made with an API key and the key does not have the
|
|
44
|
-
* required permission for the target collection/operation, an error is thrown.
|
|
45
|
-
* Non-API-key requests (e.g. session auth) are allowed through.
|
|
46
|
-
*/
|
|
47
|
-
private enforceApiKeyPermission(
|
|
48
|
-
context: GraphQLResolverContext | undefined,
|
|
49
|
-
collectionSlug: string,
|
|
50
|
-
operation: ApiKeyOperation
|
|
51
|
-
): void {
|
|
52
|
-
const apiKey = context?.get?.("apiKey") as ApiKeyMasked | undefined;
|
|
53
|
-
if (!apiKey) return;
|
|
54
|
-
if (!isOperationAllowed(apiKey.permissions, collectionSlug, operation)) {
|
|
55
|
-
throw new Error(
|
|
56
|
-
`API key does not have "${operation}" permission for collection "${collectionSlug}"`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Generate complete GraphQL schema using existing DataDriver
|
|
63
|
-
*/
|
|
64
|
-
generateSchema(): GraphQLSchema {
|
|
65
|
-
// Create all types first
|
|
66
|
-
this.collections.forEach(collection => {
|
|
67
|
-
this.createEntityType(collection);
|
|
68
|
-
this.createInputType(collection);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
const queryType = this.createQueryType();
|
|
72
|
-
const mutationType = this.createMutationType();
|
|
73
|
-
|
|
74
|
-
return new GraphQLSchema({
|
|
75
|
-
query: queryType,
|
|
76
|
-
mutation: mutationType
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Create GraphQL type for an entity collection
|
|
82
|
-
*/
|
|
83
|
-
private createEntityType(collection: EntityCollection): GraphQLObjectType {
|
|
84
|
-
const typeName = this.getTypeName(collection);
|
|
85
|
-
|
|
86
|
-
if (this.typeRegistry.has(typeName)) {
|
|
87
|
-
return this.typeRegistry.get(typeName)!;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const fields: Record<string, GraphQLFieldConfig<unknown, unknown>> = {};
|
|
91
|
-
|
|
92
|
-
// Add ID field
|
|
93
|
-
fields.id = {
|
|
94
|
-
type: new GraphQLNonNull(GraphQLString),
|
|
95
|
-
description: "Unique identifier",
|
|
96
|
-
resolve: (source: unknown) => (source as Entity<Record<string, unknown>>).id
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
// Convert properties to GraphQL fields
|
|
100
|
-
Object.entries(collection.properties).forEach(([key, property]) => {
|
|
101
|
-
if (property.type !== "relation" && key !== "id") {
|
|
102
|
-
const fieldConfig = this.convertPropertyToField(property);
|
|
103
|
-
fieldConfig.resolve = (source: unknown) => (source as Entity<Record<string, unknown>>).values?.[key];
|
|
104
|
-
fields[key] = fieldConfig;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
const entityType = new GraphQLObjectType({
|
|
109
|
-
name: typeName,
|
|
110
|
-
description: collection.description || `${collection.singularName} entity`,
|
|
111
|
-
fields: () => fields
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
this.typeRegistry.set(typeName, entityType);
|
|
115
|
-
return entityType;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private convertPropertyToField(property: Property): GraphQLFieldConfig<unknown, unknown> {
|
|
119
|
-
let type;
|
|
120
|
-
|
|
121
|
-
switch (property.type) {
|
|
122
|
-
case "binary":
|
|
123
|
-
case "string":
|
|
124
|
-
type = GraphQLString;
|
|
125
|
-
break;
|
|
126
|
-
case "number":
|
|
127
|
-
type = GraphQLFloat;
|
|
128
|
-
break;
|
|
129
|
-
case "boolean":
|
|
130
|
-
type = GraphQLBoolean;
|
|
131
|
-
break;
|
|
132
|
-
case "date":
|
|
133
|
-
type = GraphQLString;
|
|
134
|
-
break;
|
|
135
|
-
case "array":
|
|
136
|
-
type = new GraphQLList(GraphQLString);
|
|
137
|
-
break;
|
|
138
|
-
case "vector":
|
|
139
|
-
type = new GraphQLList(GraphQLFloat);
|
|
140
|
-
break;
|
|
141
|
-
default:
|
|
142
|
-
type = GraphQLString;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
type: property.validation?.required ? new GraphQLNonNull(type) : type,
|
|
147
|
-
description: property.name || property.description
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private createInputType(collection: EntityCollection): GraphQLInputObjectType {
|
|
152
|
-
const typeName = `${this.getTypeName(collection)}Input`;
|
|
153
|
-
|
|
154
|
-
if (this.inputTypeRegistry.has(typeName)) {
|
|
155
|
-
return this.inputTypeRegistry.get(typeName)!;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const fields: Record<string, GraphQLInputFieldConfig> = {};
|
|
159
|
-
|
|
160
|
-
Object.entries(collection.properties).forEach(([key, property]) => {
|
|
161
|
-
if (property.type !== "relation") {
|
|
162
|
-
fields[key] = {
|
|
163
|
-
type: this.convertPropertyToInputType(property)
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const inputType = new GraphQLInputObjectType({
|
|
169
|
-
name: typeName,
|
|
170
|
-
description: `Input for creating/updating ${collection.singularName}`,
|
|
171
|
-
fields
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
this.inputTypeRegistry.set(typeName, inputType);
|
|
175
|
-
return inputType;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
private convertPropertyToInputType(property: Property) {
|
|
179
|
-
switch (property.type) {
|
|
180
|
-
case "binary":
|
|
181
|
-
case "string":
|
|
182
|
-
return GraphQLString;
|
|
183
|
-
case "number":
|
|
184
|
-
return GraphQLFloat;
|
|
185
|
-
case "boolean":
|
|
186
|
-
return GraphQLBoolean;
|
|
187
|
-
case "date":
|
|
188
|
-
return GraphQLString;
|
|
189
|
-
case "array":
|
|
190
|
-
return new GraphQLList(GraphQLString);
|
|
191
|
-
case "vector":
|
|
192
|
-
return new GraphQLList(GraphQLFloat);
|
|
193
|
-
default:
|
|
194
|
-
return GraphQLString;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Create Query type using existing DataDriver methods
|
|
200
|
-
*/
|
|
201
|
-
private createQueryType(): GraphQLObjectType {
|
|
202
|
-
const fields: Record<string, GraphQLFieldConfig<unknown, unknown>> = {};
|
|
203
|
-
|
|
204
|
-
this.collections.forEach(collection => {
|
|
205
|
-
const typeName = this.getTypeName(collection);
|
|
206
|
-
const entityType = this.typeRegistry.get(typeName);
|
|
207
|
-
|
|
208
|
-
if (!entityType) return;
|
|
209
|
-
|
|
210
|
-
// Single entity query - uses existing fetchEntity
|
|
211
|
-
fields[this.getSingleQueryName(collection)] = {
|
|
212
|
-
type: entityType,
|
|
213
|
-
args: {
|
|
214
|
-
id: { type: new GraphQLNonNull(GraphQLString) }
|
|
215
|
-
},
|
|
216
|
-
resolve: async (_, args, context: unknown) => {
|
|
217
|
-
const ctx = context as GraphQLResolverContext | undefined;
|
|
218
|
-
this.enforceApiKeyPermission(ctx, collection.slug, "read");
|
|
219
|
-
const ds = ctx?.get?.("driver") as DataDriver | undefined ?? (ctx as Record<string, unknown> | undefined)?.driver as DataDriver | undefined;
|
|
220
|
-
if (!ds) throw new Error("Scoped driver not available");
|
|
221
|
-
const entity = await ds.fetchEntity({
|
|
222
|
-
path: collection.slug,
|
|
223
|
-
entityId: args.id,
|
|
224
|
-
collection
|
|
225
|
-
});
|
|
226
|
-
return entity;
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// List query - uses existing fetchCollection
|
|
231
|
-
fields[this.getListQueryName(collection)] = {
|
|
232
|
-
type: new GraphQLList(entityType),
|
|
233
|
-
args: {
|
|
234
|
-
limit: { type: GraphQLInt,
|
|
235
|
-
defaultValue: 20 },
|
|
236
|
-
offset: { type: GraphQLInt,
|
|
237
|
-
defaultValue: 0 },
|
|
238
|
-
where: { type: GraphQLString },
|
|
239
|
-
orderBy: { type: GraphQLString }
|
|
240
|
-
},
|
|
241
|
-
resolve: async (_, args, context: unknown) => {
|
|
242
|
-
const ctx = context as GraphQLResolverContext | undefined;
|
|
243
|
-
this.enforceApiKeyPermission(ctx, collection.slug, "read");
|
|
244
|
-
const ds = ctx?.get?.("driver") as DataDriver | undefined ?? (ctx as Record<string, unknown> | undefined)?.driver as DataDriver | undefined;
|
|
245
|
-
if (!ds) throw new Error("Scoped driver not available");
|
|
246
|
-
let filter: FetchCollectionProps["filter"] | undefined;
|
|
247
|
-
if (args.where) {
|
|
248
|
-
try {
|
|
249
|
-
const parsed = JSON.parse(args.where);
|
|
250
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
251
|
-
throw new Error("Filter must be a JSON object");
|
|
252
|
-
}
|
|
253
|
-
filter = parsed;
|
|
254
|
-
} catch (e) {
|
|
255
|
-
throw new Error(`Invalid 'where' filter: ${e instanceof Error ? e.message : "malformed JSON"}`);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
const entities = await ds.fetchCollection({
|
|
259
|
-
path: collection.slug,
|
|
260
|
-
collection,
|
|
261
|
-
filter,
|
|
262
|
-
limit: args.limit,
|
|
263
|
-
startAfter: args.offset ? String(args.offset) : undefined
|
|
264
|
-
});
|
|
265
|
-
return entities;
|
|
266
|
-
}
|
|
267
|
-
};
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
return new GraphQLObjectType({
|
|
271
|
-
name: "Query",
|
|
272
|
-
fields
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Create Mutation type using existing DataDriver methods
|
|
278
|
-
*/
|
|
279
|
-
private createMutationType(): GraphQLObjectType {
|
|
280
|
-
const fields: Record<string, GraphQLFieldConfig<unknown, unknown>> = {};
|
|
281
|
-
|
|
282
|
-
this.collections.forEach(collection => {
|
|
283
|
-
const typeName = this.getTypeName(collection);
|
|
284
|
-
const entityType = this.typeRegistry.get(typeName);
|
|
285
|
-
const inputType = this.inputTypeRegistry.get(`${typeName}Input`);
|
|
286
|
-
|
|
287
|
-
if (!entityType || !inputType) return;
|
|
288
|
-
|
|
289
|
-
// Create mutation - uses existing saveEntity
|
|
290
|
-
fields[`create${typeName}`] = {
|
|
291
|
-
type: entityType,
|
|
292
|
-
args: {
|
|
293
|
-
input: { type: new GraphQLNonNull(inputType) }
|
|
294
|
-
},
|
|
295
|
-
resolve: async (_, args, context: unknown) => {
|
|
296
|
-
const ctx = context as GraphQLResolverContext | undefined;
|
|
297
|
-
this.enforceApiKeyPermission(ctx, collection.slug, "write");
|
|
298
|
-
const ds = ctx?.get?.("driver") as DataDriver | undefined ?? (ctx as Record<string, unknown> | undefined)?.driver as DataDriver | undefined;
|
|
299
|
-
if (!ds) throw new Error("Scoped driver not available");
|
|
300
|
-
const path = collection.slug;
|
|
301
|
-
const entity = await ds.saveEntity({
|
|
302
|
-
path,
|
|
303
|
-
values: args.input,
|
|
304
|
-
collection,
|
|
305
|
-
status: "new"
|
|
306
|
-
});
|
|
307
|
-
return entity;
|
|
308
|
-
}
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
// Update mutation - uses existing saveEntity
|
|
312
|
-
fields[`update${typeName}`] = {
|
|
313
|
-
type: entityType,
|
|
314
|
-
args: {
|
|
315
|
-
id: { type: new GraphQLNonNull(GraphQLString) },
|
|
316
|
-
input: { type: new GraphQLNonNull(inputType) }
|
|
317
|
-
},
|
|
318
|
-
resolve: async (_, args, context: unknown) => {
|
|
319
|
-
const ctx = context as GraphQLResolverContext | undefined;
|
|
320
|
-
this.enforceApiKeyPermission(ctx, collection.slug, "write");
|
|
321
|
-
const ds = ctx?.get?.("driver") as DataDriver | undefined ?? (ctx as Record<string, unknown> | undefined)?.driver as DataDriver | undefined;
|
|
322
|
-
if (!ds) throw new Error("Scoped driver not available");
|
|
323
|
-
const entity = await ds.saveEntity({
|
|
324
|
-
path: collection.slug,
|
|
325
|
-
entityId: args.id,
|
|
326
|
-
values: args.input,
|
|
327
|
-
collection,
|
|
328
|
-
status: "existing"
|
|
329
|
-
});
|
|
330
|
-
return entity;
|
|
331
|
-
}
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
// Delete mutation - uses existing deleteEntity
|
|
335
|
-
fields[`delete${typeName}`] = {
|
|
336
|
-
type: GraphQLBoolean,
|
|
337
|
-
args: {
|
|
338
|
-
id: { type: new GraphQLNonNull(GraphQLString) }
|
|
339
|
-
},
|
|
340
|
-
resolve: async (_, args, context: unknown) => {
|
|
341
|
-
const ctx = context as GraphQLResolverContext | undefined;
|
|
342
|
-
this.enforceApiKeyPermission(ctx, collection.slug, "delete");
|
|
343
|
-
try {
|
|
344
|
-
const ds = ctx?.get?.("driver") as DataDriver | undefined ?? (ctx as Record<string, unknown> | undefined)?.driver as DataDriver | undefined;
|
|
345
|
-
if (!ds) throw new Error("Scoped driver not available");
|
|
346
|
-
const existingEntity = await ds.fetchEntity({
|
|
347
|
-
path: collection.slug,
|
|
348
|
-
entityId: args.id,
|
|
349
|
-
collection
|
|
350
|
-
});
|
|
351
|
-
if (!existingEntity) return false;
|
|
352
|
-
await ds.deleteEntity({
|
|
353
|
-
entity: existingEntity,
|
|
354
|
-
collection
|
|
355
|
-
});
|
|
356
|
-
return true;
|
|
357
|
-
} catch {
|
|
358
|
-
return false;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
return new GraphQLObjectType({
|
|
365
|
-
name: "Mutation",
|
|
366
|
-
fields
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
// Helper methods
|
|
371
|
-
private getTypeName(collection: EntityCollection): string {
|
|
372
|
-
return collection.singularName?.replace(/\s+/g, "") ||
|
|
373
|
-
collection.name.slice(0, -1).replace(/\s+/g, "");
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
private getSingleQueryName(collection: EntityCollection): string {
|
|
377
|
-
return this.getTypeName(collection).toLowerCase();
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
private getListQueryName(collection: EntityCollection): string {
|
|
381
|
-
return collection.slug;
|
|
382
|
-
}
|
|
383
|
-
}
|
package/src/api/graphql/index.ts
DELETED
package/src/api/index.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* API generation infrastructure for Rebase
|
|
3
|
-
* Automatically generates GraphQL and REST APIs from EntityCollection definitions
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export * from "./graphql";
|
|
7
|
-
export * from "./rest";
|
|
8
|
-
|
|
9
|
-
export * from "./types";
|
|
10
|
-
export * from "./errors";
|
|
11
|
-
export * from "./server";
|
package/src/api/logs-routes.ts
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { Hono } from "hono";
|
|
2
|
-
import type { MiddlewareHandler } from "hono";
|
|
3
|
-
import type { HonoEnv } from "./types";
|
|
4
|
-
|
|
5
|
-
export interface LogEntry {
|
|
6
|
-
id: string;
|
|
7
|
-
timestamp: string;
|
|
8
|
-
level: "debug" | "info" | "warn" | "error";
|
|
9
|
-
source: "api" | "auth" | "storage" | "realtime" | "system";
|
|
10
|
-
message: string;
|
|
11
|
-
metadata?: Record<string, unknown>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
class LogRingBuffer {
|
|
15
|
-
private buffer: LogEntry[] = [];
|
|
16
|
-
private maxSize: number;
|
|
17
|
-
private idCounter = 0;
|
|
18
|
-
|
|
19
|
-
constructor(maxSize = 10000) {
|
|
20
|
-
this.maxSize = maxSize;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
push(entry: Omit<LogEntry, "id">): void {
|
|
24
|
-
const id = `log_${++this.idCounter}`;
|
|
25
|
-
this.buffer.push({ ...entry,
|
|
26
|
-
id });
|
|
27
|
-
if (this.buffer.length > this.maxSize) {
|
|
28
|
-
this.buffer.shift();
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
query(options: {
|
|
33
|
-
level?: string;
|
|
34
|
-
source?: string;
|
|
35
|
-
search?: string;
|
|
36
|
-
limit?: number;
|
|
37
|
-
offset?: number;
|
|
38
|
-
since?: string;
|
|
39
|
-
}): { entries: LogEntry[]; total: number } {
|
|
40
|
-
let filtered = this.buffer;
|
|
41
|
-
|
|
42
|
-
if (options.level) {
|
|
43
|
-
filtered = filtered.filter(e => e.level === options.level);
|
|
44
|
-
}
|
|
45
|
-
if (options.source) {
|
|
46
|
-
filtered = filtered.filter(e => e.source === options.source);
|
|
47
|
-
}
|
|
48
|
-
if (options.search) {
|
|
49
|
-
const searchLower = options.search.toLowerCase();
|
|
50
|
-
filtered = filtered.filter(e => e.message.toLowerCase().includes(searchLower));
|
|
51
|
-
}
|
|
52
|
-
if (options.since) {
|
|
53
|
-
const sinceValue = options.since;
|
|
54
|
-
filtered = filtered.filter(e => e.timestamp >= sinceValue);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Newest first
|
|
58
|
-
const sorted = [...filtered].reverse();
|
|
59
|
-
const total = sorted.length;
|
|
60
|
-
const limit = options.limit || 100;
|
|
61
|
-
const offset = options.offset || 0;
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
entries: sorted.slice(offset, offset + limit),
|
|
65
|
-
total
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
getLatest(count = 50): LogEntry[] {
|
|
70
|
-
return this.buffer.slice(-count).reverse();
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Global singleton
|
|
75
|
-
export const logBuffer = new LogRingBuffer();
|
|
76
|
-
|
|
77
|
-
/** Add a log entry */
|
|
78
|
-
export function addLog(
|
|
79
|
-
level: LogEntry["level"],
|
|
80
|
-
source: LogEntry["source"],
|
|
81
|
-
message: string,
|
|
82
|
-
metadata?: Record<string, unknown>
|
|
83
|
-
): void {
|
|
84
|
-
logBuffer.push({
|
|
85
|
-
timestamp: new Date().toISOString(),
|
|
86
|
-
level,
|
|
87
|
-
source,
|
|
88
|
-
message,
|
|
89
|
-
metadata
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** Hono middleware to log API requests */
|
|
94
|
-
export function logMiddleware(): MiddlewareHandler<HonoEnv> {
|
|
95
|
-
return async (c, next) => {
|
|
96
|
-
const start = Date.now();
|
|
97
|
-
await next();
|
|
98
|
-
const duration = Date.now() - start;
|
|
99
|
-
const reqId = c.get("requestId");
|
|
100
|
-
addLog("info", "api", `${c.req.method} ${c.req.path} ${c.res.status} ${duration}ms`, {
|
|
101
|
-
method: c.req.method,
|
|
102
|
-
path: c.req.path,
|
|
103
|
-
status: c.res.status,
|
|
104
|
-
duration,
|
|
105
|
-
...(reqId && { requestId: reqId })
|
|
106
|
-
});
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const app = new Hono<HonoEnv>();
|
|
111
|
-
|
|
112
|
-
// GET /api/logs — Query logs
|
|
113
|
-
app.get("/", (c) => {
|
|
114
|
-
const query = c.req.query();
|
|
115
|
-
const result = logBuffer.query({
|
|
116
|
-
level: query.level,
|
|
117
|
-
source: query.source,
|
|
118
|
-
search: query.search,
|
|
119
|
-
limit: query.limit ? parseInt(query.limit) : undefined,
|
|
120
|
-
offset: query.offset ? parseInt(query.offset) : undefined,
|
|
121
|
-
since: query.since
|
|
122
|
-
});
|
|
123
|
-
return c.json(result);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// GET /api/logs/latest — Get latest logs (for real-time)
|
|
127
|
-
app.get("/latest", (c) => {
|
|
128
|
-
const count = parseInt(c.req.query("count") || "50");
|
|
129
|
-
return c.json({ entries: logBuffer.getLatest(count) });
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
export default app;
|