@rebasepro/server-core 0.0.1-canary.09e5ec5
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/LICENSE +6 -0
- package/README.md +40 -0
- package/build-errors.txt +52 -0
- package/coverage/clover.xml +3739 -0
- package/coverage/coverage-final.json +31 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +266 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +210 -0
- package/coverage/lcov-report/src/api/ast-schema-editor.ts.html +952 -0
- package/coverage/lcov-report/src/api/errors.ts.html +472 -0
- package/coverage/lcov-report/src/api/graphql/graphql-schema-generator.ts.html +1069 -0
- package/coverage/lcov-report/src/api/graphql/index.html +116 -0
- package/coverage/lcov-report/src/api/index.html +176 -0
- package/coverage/lcov-report/src/api/openapi-generator.ts.html +565 -0
- package/coverage/lcov-report/src/api/rest/api-generator.ts.html +994 -0
- package/coverage/lcov-report/src/api/rest/index.html +131 -0
- package/coverage/lcov-report/src/api/rest/query-parser.ts.html +550 -0
- package/coverage/lcov-report/src/api/schema-editor-routes.ts.html +202 -0
- package/coverage/lcov-report/src/api/server.ts.html +823 -0
- package/coverage/lcov-report/src/auth/admin-routes.ts.html +973 -0
- package/coverage/lcov-report/src/auth/index.html +176 -0
- package/coverage/lcov-report/src/auth/jwt.ts.html +574 -0
- package/coverage/lcov-report/src/auth/middleware.ts.html +745 -0
- package/coverage/lcov-report/src/auth/password.ts.html +310 -0
- package/coverage/lcov-report/src/auth/services.ts.html +2074 -0
- package/coverage/lcov-report/src/collections/index.html +116 -0
- package/coverage/lcov-report/src/collections/loader.ts.html +232 -0
- package/coverage/lcov-report/src/db/auth-schema.ts.html +523 -0
- package/coverage/lcov-report/src/db/data-transformer.ts.html +1753 -0
- package/coverage/lcov-report/src/db/entityService.ts.html +700 -0
- package/coverage/lcov-report/src/db/index.html +146 -0
- package/coverage/lcov-report/src/db/services/EntityFetchService.ts.html +4048 -0
- package/coverage/lcov-report/src/db/services/EntityPersistService.ts.html +883 -0
- package/coverage/lcov-report/src/db/services/RelationService.ts.html +3121 -0
- package/coverage/lcov-report/src/db/services/entity-helpers.ts.html +442 -0
- package/coverage/lcov-report/src/db/services/index.html +176 -0
- package/coverage/lcov-report/src/db/services/index.ts.html +124 -0
- package/coverage/lcov-report/src/generate-drizzle-schema-logic.ts.html +1960 -0
- package/coverage/lcov-report/src/index.html +116 -0
- package/coverage/lcov-report/src/services/driver-registry.ts.html +631 -0
- package/coverage/lcov-report/src/services/index.html +131 -0
- package/coverage/lcov-report/src/services/postgresDataDriver.ts.html +3025 -0
- package/coverage/lcov-report/src/storage/LocalStorageController.ts.html +1189 -0
- package/coverage/lcov-report/src/storage/S3StorageController.ts.html +970 -0
- package/coverage/lcov-report/src/storage/index.html +161 -0
- package/coverage/lcov-report/src/storage/storage-registry.ts.html +646 -0
- package/coverage/lcov-report/src/storage/types.ts.html +451 -0
- package/coverage/lcov-report/src/utils/drizzle-conditions.ts.html +3082 -0
- package/coverage/lcov-report/src/utils/index.html +116 -0
- package/coverage/lcov.info +7179 -0
- package/dist/common/src/collections/CollectionRegistry.d.ts +56 -0
- package/dist/common/src/collections/index.d.ts +1 -0
- package/dist/common/src/data/buildRebaseData.d.ts +14 -0
- package/dist/common/src/index.d.ts +3 -0
- package/dist/common/src/util/builders.d.ts +57 -0
- package/dist/common/src/util/callbacks.d.ts +6 -0
- package/dist/common/src/util/collections.d.ts +11 -0
- package/dist/common/src/util/common.d.ts +2 -0
- package/dist/common/src/util/conditions.d.ts +26 -0
- package/dist/common/src/util/entities.d.ts +58 -0
- package/dist/common/src/util/enums.d.ts +3 -0
- package/dist/common/src/util/index.d.ts +16 -0
- package/dist/common/src/util/navigation_from_path.d.ts +34 -0
- package/dist/common/src/util/navigation_utils.d.ts +20 -0
- package/dist/common/src/util/parent_references_from_path.d.ts +6 -0
- package/dist/common/src/util/paths.d.ts +14 -0
- package/dist/common/src/util/permissions.d.ts +5 -0
- package/dist/common/src/util/references.d.ts +2 -0
- package/dist/common/src/util/relations.d.ts +22 -0
- package/dist/common/src/util/resolutions.d.ts +72 -0
- package/dist/common/src/util/storage.d.ts +24 -0
- package/dist/index-DXVBFp5V.js +37 -0
- package/dist/index-DXVBFp5V.js.map +1 -0
- package/dist/index.es.js +49934 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.umd.js +49968 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/server-core/src/api/ast-schema-editor.d.ts +21 -0
- package/dist/server-core/src/api/collections_for_test/callbacks_test_collection.d.ts +2 -0
- package/dist/server-core/src/api/errors.d.ts +35 -0
- package/dist/server-core/src/api/graphql/graphql-schema-generator.d.ts +35 -0
- package/dist/server-core/src/api/graphql/index.d.ts +1 -0
- package/dist/server-core/src/api/index.d.ts +9 -0
- package/dist/server-core/src/api/openapi-generator.d.ts +16 -0
- package/dist/server-core/src/api/rest/api-generator.d.ts +64 -0
- package/dist/server-core/src/api/rest/index.d.ts +1 -0
- package/dist/server-core/src/api/rest/query-parser.d.ts +9 -0
- package/dist/server-core/src/api/schema-editor-routes.d.ts +3 -0
- package/dist/server-core/src/api/server.d.ts +40 -0
- package/dist/server-core/src/api/types.d.ts +90 -0
- package/dist/server-core/src/auth/admin-routes.d.ts +16 -0
- package/dist/server-core/src/auth/apple-oauth.d.ts +30 -0
- package/dist/server-core/src/auth/bitbucket-oauth.d.ts +11 -0
- package/dist/server-core/src/auth/discord-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/facebook-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/github-oauth.d.ts +15 -0
- package/dist/server-core/src/auth/gitlab-oauth.d.ts +13 -0
- package/dist/server-core/src/auth/google-oauth.d.ts +14 -0
- package/dist/server-core/src/auth/index.d.ts +23 -0
- package/dist/server-core/src/auth/interfaces.d.ts +309 -0
- package/dist/server-core/src/auth/jwt.d.ts +43 -0
- package/dist/server-core/src/auth/linkedin-oauth.d.ts +18 -0
- package/dist/server-core/src/auth/microsoft-oauth.d.ts +16 -0
- package/dist/server-core/src/auth/middleware.d.ts +81 -0
- package/dist/server-core/src/auth/password.d.ts +22 -0
- package/dist/server-core/src/auth/rate-limiter.d.ts +31 -0
- package/dist/server-core/src/auth/routes.d.ts +27 -0
- package/dist/server-core/src/auth/slack-oauth.d.ts +12 -0
- package/dist/server-core/src/auth/spotify-oauth.d.ts +12 -0
- package/dist/server-core/src/auth/twitter-oauth.d.ts +18 -0
- package/dist/server-core/src/bootstrappers/index.d.ts +0 -0
- package/dist/server-core/src/collections/BackendCollectionRegistry.d.ts +13 -0
- package/dist/server-core/src/collections/loader.d.ts +5 -0
- package/dist/server-core/src/cron/cron-loader.d.ts +17 -0
- package/dist/server-core/src/cron/cron-routes.d.ts +14 -0
- package/dist/server-core/src/cron/cron-scheduler.d.ts +61 -0
- package/dist/server-core/src/cron/cron-store.d.ts +32 -0
- package/dist/server-core/src/cron/index.d.ts +6 -0
- package/dist/server-core/src/db/interfaces.d.ts +18 -0
- package/dist/server-core/src/email/index.d.ts +6 -0
- package/dist/server-core/src/email/smtp-email-service.d.ts +25 -0
- package/dist/server-core/src/email/templates.d.ts +42 -0
- package/dist/server-core/src/email/types.d.ts +107 -0
- package/dist/server-core/src/functions/function-loader.d.ts +17 -0
- package/dist/server-core/src/functions/function-routes.d.ts +10 -0
- package/dist/server-core/src/functions/index.d.ts +3 -0
- package/dist/server-core/src/history/history-routes.d.ts +23 -0
- package/dist/server-core/src/history/index.d.ts +1 -0
- package/dist/server-core/src/index.d.ts +29 -0
- package/dist/server-core/src/init.d.ts +159 -0
- package/dist/server-core/src/serve-spa.d.ts +30 -0
- package/dist/server-core/src/services/driver-registry.d.ts +78 -0
- package/dist/server-core/src/singleton.d.ts +35 -0
- package/dist/server-core/src/storage/LocalStorageController.d.ts +46 -0
- package/dist/server-core/src/storage/S3StorageController.d.ts +36 -0
- package/dist/server-core/src/storage/index.d.ts +25 -0
- package/dist/server-core/src/storage/routes.d.ts +38 -0
- package/dist/server-core/src/storage/storage-registry.d.ts +78 -0
- package/dist/server-core/src/storage/types.d.ts +103 -0
- package/dist/server-core/src/types/index.d.ts +11 -0
- package/dist/server-core/src/utils/dev-port.d.ts +35 -0
- package/dist/server-core/src/utils/logger.d.ts +31 -0
- package/dist/server-core/src/utils/logging.d.ts +9 -0
- package/dist/server-core/src/utils/request-logger.d.ts +19 -0
- package/dist/server-core/src/utils/sql.d.ts +27 -0
- package/dist/types/src/controllers/analytics_controller.d.ts +7 -0
- package/dist/types/src/controllers/auth.d.ts +119 -0
- package/dist/types/src/controllers/client.d.ts +170 -0
- package/dist/types/src/controllers/collection_registry.d.ts +45 -0
- package/dist/types/src/controllers/customization_controller.d.ts +60 -0
- package/dist/types/src/controllers/data.d.ts +168 -0
- package/dist/types/src/controllers/data_driver.d.ts +160 -0
- package/dist/types/src/controllers/database_admin.d.ts +11 -0
- package/dist/types/src/controllers/dialogs_controller.d.ts +36 -0
- package/dist/types/src/controllers/effective_role.d.ts +4 -0
- package/dist/types/src/controllers/email.d.ts +34 -0
- package/dist/types/src/controllers/index.d.ts +18 -0
- package/dist/types/src/controllers/local_config_persistence.d.ts +20 -0
- package/dist/types/src/controllers/navigation.d.ts +213 -0
- package/dist/types/src/controllers/registry.d.ts +54 -0
- package/dist/types/src/controllers/side_dialogs_controller.d.ts +67 -0
- package/dist/types/src/controllers/side_entity_controller.d.ts +90 -0
- package/dist/types/src/controllers/snackbar.d.ts +24 -0
- package/dist/types/src/controllers/storage.d.ts +171 -0
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/rebase_context.d.ts +105 -0
- package/dist/types/src/types/backend.d.ts +536 -0
- package/dist/types/src/types/builders.d.ts +15 -0
- package/dist/types/src/types/chips.d.ts +5 -0
- package/dist/types/src/types/collections.d.ts +856 -0
- package/dist/types/src/types/cron.d.ts +102 -0
- package/dist/types/src/types/data_source.d.ts +64 -0
- package/dist/types/src/types/entities.d.ts +145 -0
- package/dist/types/src/types/entity_actions.d.ts +98 -0
- package/dist/types/src/types/entity_callbacks.d.ts +173 -0
- package/dist/types/src/types/entity_link_builder.d.ts +7 -0
- package/dist/types/src/types/entity_overrides.d.ts +10 -0
- package/dist/types/src/types/entity_views.d.ts +61 -0
- package/dist/types/src/types/export_import.d.ts +21 -0
- package/dist/types/src/types/index.d.ts +23 -0
- package/dist/types/src/types/locales.d.ts +4 -0
- package/dist/types/src/types/modify_collections.d.ts +5 -0
- package/dist/types/src/types/plugins.d.ts +279 -0
- package/dist/types/src/types/properties.d.ts +1176 -0
- package/dist/types/src/types/property_config.d.ts +70 -0
- package/dist/types/src/types/relations.d.ts +336 -0
- package/dist/types/src/types/slots.d.ts +252 -0
- package/dist/types/src/types/translations.d.ts +870 -0
- package/dist/types/src/types/user_management_delegate.d.ts +121 -0
- package/dist/types/src/types/websockets.d.ts +78 -0
- package/dist/types/src/users/index.d.ts +2 -0
- package/dist/types/src/users/roles.d.ts +22 -0
- package/dist/types/src/users/user.d.ts +46 -0
- package/history_diff.log +385 -0
- package/jest.config.cjs +16 -0
- package/package.json +86 -0
- package/scratch.ts +9 -0
- package/src/api/ast-schema-editor.ts +289 -0
- package/src/api/collections_for_test/callbacks_test_collection.ts +60 -0
- package/src/api/errors.ts +179 -0
- package/src/api/graphql/graphql-schema-generator.ts +336 -0
- package/src/api/graphql/index.ts +2 -0
- package/src/api/index.ts +11 -0
- package/src/api/openapi-generator.ts +715 -0
- package/src/api/rest/api-generator.ts +472 -0
- package/src/api/rest/index.ts +2 -0
- package/src/api/rest/query-parser.ts +155 -0
- package/src/api/schema-editor-routes.ts +41 -0
- package/src/api/server.ts +248 -0
- package/src/api/types.ts +90 -0
- package/src/auth/admin-routes.ts +529 -0
- package/src/auth/apple-oauth.ts +130 -0
- package/src/auth/bitbucket-oauth.ts +82 -0
- package/src/auth/discord-oauth.ts +83 -0
- package/src/auth/facebook-oauth.ts +72 -0
- package/src/auth/github-oauth.ts +110 -0
- package/src/auth/gitlab-oauth.ts +70 -0
- package/src/auth/google-oauth.ts +48 -0
- package/src/auth/index.ts +34 -0
- package/src/auth/interfaces.ts +363 -0
- package/src/auth/jwt.ts +181 -0
- package/src/auth/linkedin-oauth.ts +81 -0
- package/src/auth/microsoft-oauth.ts +88 -0
- package/src/auth/middleware.ts +384 -0
- package/src/auth/password.ts +77 -0
- package/src/auth/rate-limiter.ts +129 -0
- package/src/auth/routes.ts +788 -0
- package/src/auth/slack-oauth.ts +71 -0
- package/src/auth/spotify-oauth.ts +67 -0
- package/src/auth/twitter-oauth.ts +120 -0
- package/src/bootstrappers/index.ts +1 -0
- package/src/collections/BackendCollectionRegistry.ts +20 -0
- package/src/collections/loader.ts +49 -0
- package/src/cron/cron-loader.ts +89 -0
- package/src/cron/cron-routes.test.ts +265 -0
- package/src/cron/cron-routes.ts +85 -0
- package/src/cron/cron-scheduler.test.ts +421 -0
- package/src/cron/cron-scheduler.ts +413 -0
- package/src/cron/cron-store.ts +163 -0
- package/src/cron/index.ts +6 -0
- package/src/db/interfaces.ts +60 -0
- package/src/email/index.ts +18 -0
- package/src/email/smtp-email-service.ts +91 -0
- package/src/email/templates.ts +388 -0
- package/src/email/types.ts +105 -0
- package/src/functions/function-loader.ts +119 -0
- package/src/functions/function-routes.ts +31 -0
- package/src/functions/index.ts +3 -0
- package/src/history/history-routes.ts +129 -0
- package/src/history/index.ts +2 -0
- package/src/index.ts +66 -0
- package/src/init.ts +727 -0
- package/src/serve-spa.ts +81 -0
- package/src/services/driver-registry.ts +182 -0
- package/src/singleton.test.ts +28 -0
- package/src/singleton.ts +70 -0
- package/src/storage/LocalStorageController.ts +365 -0
- package/src/storage/S3StorageController.ts +298 -0
- package/src/storage/index.ts +43 -0
- package/src/storage/routes.ts +264 -0
- package/src/storage/storage-registry.ts +187 -0
- package/src/storage/types.ts +134 -0
- package/src/types/index.ts +27 -0
- package/src/utils/dev-port.ts +176 -0
- package/src/utils/logger.ts +143 -0
- package/src/utils/logging.ts +38 -0
- package/src/utils/request-logger.ts +66 -0
- package/src/utils/sql.ts +38 -0
- package/test/admin-routes.test.ts +640 -0
- package/test/api-generator.test.ts +501 -0
- package/test/ast-schema-editor.test.ts +63 -0
- package/test/auth-middleware-hono.test.ts +556 -0
- package/test/auth-routes.test.ts +1047 -0
- package/test/driver-registry.test.ts +282 -0
- package/test/error-propagation.test.ts +226 -0
- package/test/errors-hono.test.ts +133 -0
- package/test/errors.test.ts +155 -0
- package/test/jwt-security.test.ts +182 -0
- package/test/jwt.test.ts +324 -0
- package/test/middleware.test.ts +300 -0
- package/test/password.test.ts +165 -0
- package/test/query-parser.test.ts +263 -0
- package/test/rate-limiter.test.ts +102 -0
- package/test/safe-compare.test.ts +66 -0
- package/test/singleton.test.ts +59 -0
- package/test/storage-local.test.ts +271 -0
- package/test/storage-registry.test.ts +282 -0
- package/test/storage-routes.test.ts +222 -0
- package/test/storage-s3.test.ts +304 -0
- package/test-ast.ts +28 -0
- package/test.ts +6 -0
- package/test_output.txt +1133 -0
- package/tsconfig.json +49 -0
- package/tsconfig.prod.json +20 -0
- package/vite.config.ts +80 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CronJobDefinition,
|
|
3
|
+
CronJobStatus,
|
|
4
|
+
CronJobLogEntry,
|
|
5
|
+
CronJobRunState,
|
|
6
|
+
CronJobContext
|
|
7
|
+
} from "@rebasepro/types";
|
|
8
|
+
import type { RebaseClient } from "@rebasepro/client";
|
|
9
|
+
import type { LoadedCronJob } from "./cron-loader";
|
|
10
|
+
import type { CronStore } from "./cron-store";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Validates a standard cron expression.
|
|
14
|
+
*/
|
|
15
|
+
function isValidCronExpression(schedule: string): boolean {
|
|
16
|
+
if (!schedule) return false;
|
|
17
|
+
const parts = schedule.trim().split(/\s+/);
|
|
18
|
+
// Typical cron has 5 fields.
|
|
19
|
+
return parts.length === 5 && parts.every(p => p.length > 0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ─── Cron expression parser (minimal, no external dependency) ────────
|
|
23
|
+
// Supports standard 5-field cron (minute hour dom month dow).
|
|
24
|
+
// Returns the next Date after `after` that matches the expression.
|
|
25
|
+
|
|
26
|
+
function parseCronExpression(expression: string, after: Date): Date {
|
|
27
|
+
// We implement a simple forward-search. For production-grade parsing
|
|
28
|
+
// one would use a library, but we avoid adding dependencies.
|
|
29
|
+
const parts = expression.trim().split(/\s+/);
|
|
30
|
+
if (parts.length < 5) {
|
|
31
|
+
throw new Error(`Invalid cron expression: "${expression}". Expected 5 fields.`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const [minField, hourField, domField, monField, dowField] = parts;
|
|
35
|
+
|
|
36
|
+
const expand = (field: string, min: number, max: number): number[] => {
|
|
37
|
+
const results = new Set<number>();
|
|
38
|
+
for (const segment of field.split(",")) {
|
|
39
|
+
if (segment === "*") {
|
|
40
|
+
for (let i = min; i <= max; i++) results.add(i);
|
|
41
|
+
} else if (segment.includes("/")) {
|
|
42
|
+
const [rangeStr, stepStr] = segment.split("/");
|
|
43
|
+
const step = parseInt(stepStr, 10);
|
|
44
|
+
let start = min;
|
|
45
|
+
let end = max;
|
|
46
|
+
if (rangeStr !== "*") {
|
|
47
|
+
if (rangeStr.includes("-")) {
|
|
48
|
+
const [a, b] = rangeStr.split("-").map(Number);
|
|
49
|
+
start = a;
|
|
50
|
+
end = b;
|
|
51
|
+
} else {
|
|
52
|
+
start = parseInt(rangeStr, 10);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (let i = start; i <= end; i += step) results.add(i);
|
|
56
|
+
} else if (segment.includes("-")) {
|
|
57
|
+
const [a, b] = segment.split("-").map(Number);
|
|
58
|
+
for (let i = a; i <= b; i++) results.add(i);
|
|
59
|
+
} else {
|
|
60
|
+
results.add(parseInt(segment, 10));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return [...results].sort((a, b) => a - b);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const minutes = expand(minField, 0, 59);
|
|
67
|
+
const hours = expand(hourField, 0, 23);
|
|
68
|
+
const doms = expand(domField, 1, 31);
|
|
69
|
+
const months = expand(monField, 1, 12);
|
|
70
|
+
const dows = expand(dowField, 0, 6); // 0=Sunday
|
|
71
|
+
|
|
72
|
+
// Forward-search from `after + 1 minute`
|
|
73
|
+
const candidate = new Date(after);
|
|
74
|
+
candidate.setSeconds(0, 0);
|
|
75
|
+
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
76
|
+
|
|
77
|
+
const maxIterations = 525960; // ~1 year in minutes
|
|
78
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
79
|
+
const month = candidate.getMonth() + 1; // 1-12
|
|
80
|
+
const dom = candidate.getDate();
|
|
81
|
+
const dow = candidate.getDay(); // 0=Sunday
|
|
82
|
+
const hour = candidate.getHours();
|
|
83
|
+
const minute = candidate.getMinutes();
|
|
84
|
+
|
|
85
|
+
if (
|
|
86
|
+
months.includes(month) &&
|
|
87
|
+
doms.includes(dom) &&
|
|
88
|
+
dows.includes(dow) &&
|
|
89
|
+
hours.includes(hour) &&
|
|
90
|
+
minutes.includes(minute)
|
|
91
|
+
) {
|
|
92
|
+
return candidate;
|
|
93
|
+
}
|
|
94
|
+
candidate.setMinutes(candidate.getMinutes() + 1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Fallback — should not happen with valid expressions
|
|
98
|
+
const fallback = new Date(after);
|
|
99
|
+
fallback.setMinutes(fallback.getMinutes() + 1);
|
|
100
|
+
return fallback;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ─── In-memory ring buffer for logs ──────────────────────────────────
|
|
104
|
+
|
|
105
|
+
const MAX_LOGS_PER_JOB = 50;
|
|
106
|
+
|
|
107
|
+
// ─── CronScheduler ───────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
interface RegisteredJob {
|
|
110
|
+
id: string;
|
|
111
|
+
definition: CronJobDefinition;
|
|
112
|
+
enabled: boolean;
|
|
113
|
+
state: CronJobRunState;
|
|
114
|
+
lastRunAt?: Date;
|
|
115
|
+
nextRunAt?: Date;
|
|
116
|
+
lastDurationMs?: number;
|
|
117
|
+
lastError?: string;
|
|
118
|
+
totalRuns: number;
|
|
119
|
+
totalFailures: number;
|
|
120
|
+
timerId?: ReturnType<typeof setTimeout>;
|
|
121
|
+
logs: CronJobLogEntry[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export class CronScheduler {
|
|
125
|
+
private jobs = new Map<string, RegisteredJob>();
|
|
126
|
+
private started = false;
|
|
127
|
+
private store?: CronStore;
|
|
128
|
+
private client?: RebaseClient;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Set the RebaseClient instance to make it available to cron job handlers.
|
|
132
|
+
*/
|
|
133
|
+
setClient(client: RebaseClient): void {
|
|
134
|
+
this.client = client;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Attach a persistence store for cron logs.
|
|
139
|
+
* When set, execution logs are written to the database after each run,
|
|
140
|
+
* and counters are seeded from the database on start.
|
|
141
|
+
*/
|
|
142
|
+
setStore(store: CronStore): void {
|
|
143
|
+
this.store = store;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Register a batch of loaded cron jobs.
|
|
148
|
+
*/
|
|
149
|
+
registerJobs(loadedJobs: LoadedCronJob[]): void {
|
|
150
|
+
for (const loaded of loadedJobs) {
|
|
151
|
+
const existing = this.jobs.get(loaded.id);
|
|
152
|
+
if (existing) {
|
|
153
|
+
console.warn(`[cron] Duplicate cron job id: "${loaded.id}". Overwriting.`);
|
|
154
|
+
this.stopJob(loaded.id);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
this.jobs.set(loaded.id, {
|
|
158
|
+
id: loaded.id,
|
|
159
|
+
definition: loaded.definition,
|
|
160
|
+
enabled: loaded.definition.enabled !== false,
|
|
161
|
+
state: loaded.definition.enabled !== false ? "idle" : "disabled",
|
|
162
|
+
totalRuns: 0,
|
|
163
|
+
totalFailures: 0,
|
|
164
|
+
logs: []
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Start the scheduler — begins ticking all enabled jobs.
|
|
171
|
+
*/
|
|
172
|
+
start(): void {
|
|
173
|
+
if (this.started) return;
|
|
174
|
+
this.started = true;
|
|
175
|
+
|
|
176
|
+
// Seed counters from DB (non-blocking — scheduler starts immediately)
|
|
177
|
+
if (this.store) {
|
|
178
|
+
this.store.fetchJobStats().then((stats) => {
|
|
179
|
+
for (const [jobId, data] of stats) {
|
|
180
|
+
const job = this.jobs.get(jobId);
|
|
181
|
+
if (job) {
|
|
182
|
+
job.totalRuns = data.totalRuns;
|
|
183
|
+
job.totalFailures = data.totalFailures;
|
|
184
|
+
if (data.lastRunAt) {
|
|
185
|
+
job.lastRunAt = new Date(data.lastRunAt);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}).catch((err) => {
|
|
190
|
+
console.warn("[cron] Failed to seed job stats from database:", err);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
for (const [id, job] of this.jobs) {
|
|
195
|
+
if (job.enabled) {
|
|
196
|
+
this.scheduleNext(id);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
console.log(`⏰ Cron scheduler started with ${this.jobs.size} job(s)`);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Stop the scheduler and clear all timers.
|
|
204
|
+
*/
|
|
205
|
+
stop(): void {
|
|
206
|
+
this.started = false;
|
|
207
|
+
for (const [id] of this.jobs) {
|
|
208
|
+
this.stopJob(id);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* List all registered jobs with their current status.
|
|
214
|
+
*/
|
|
215
|
+
listJobs(): CronJobStatus[] {
|
|
216
|
+
return [...this.jobs.values()].map((job) => this.toStatus(job));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get a single job status by ID.
|
|
221
|
+
*/
|
|
222
|
+
getJob(id: string): CronJobStatus | undefined {
|
|
223
|
+
const job = this.jobs.get(id);
|
|
224
|
+
return job ? this.toStatus(job) : undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get log entries for a job.
|
|
229
|
+
*/
|
|
230
|
+
getJobLogs(id: string, limit?: number): CronJobLogEntry[] {
|
|
231
|
+
const job = this.jobs.get(id);
|
|
232
|
+
if (!job) return [];
|
|
233
|
+
const logs = [...job.logs].reverse(); // newest first
|
|
234
|
+
return limit ? logs.slice(0, limit) : logs;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get log entries for a job from the database (if store is available).
|
|
239
|
+
* Falls back to in-memory logs if no store is configured.
|
|
240
|
+
*/
|
|
241
|
+
async getJobLogsFromDb(id: string, limit?: number): Promise<CronJobLogEntry[]> {
|
|
242
|
+
if (this.store) {
|
|
243
|
+
const dbLogs = await this.store.fetchLogs(id, limit);
|
|
244
|
+
if (dbLogs.length > 0) return dbLogs;
|
|
245
|
+
}
|
|
246
|
+
// Fallback to in-memory
|
|
247
|
+
return this.getJobLogs(id, limit);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Enable or disable a job at runtime.
|
|
252
|
+
*/
|
|
253
|
+
setJobEnabled(id: string, enabled: boolean): CronJobStatus | undefined {
|
|
254
|
+
const job = this.jobs.get(id);
|
|
255
|
+
if (!job) return undefined;
|
|
256
|
+
|
|
257
|
+
job.enabled = enabled;
|
|
258
|
+
|
|
259
|
+
if (enabled && this.started) {
|
|
260
|
+
job.state = "idle";
|
|
261
|
+
this.scheduleNext(id);
|
|
262
|
+
} else if (!enabled) {
|
|
263
|
+
this.stopJob(id);
|
|
264
|
+
job.state = "disabled";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return this.toStatus(job);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Manually trigger a job execution immediately.
|
|
272
|
+
*/
|
|
273
|
+
async triggerJob(id: string): Promise<CronJobLogEntry | undefined> {
|
|
274
|
+
const job = this.jobs.get(id);
|
|
275
|
+
if (!job) return undefined;
|
|
276
|
+
return this.executeJob(job, true);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ─── Internal ────────────────────────────────────────────────────
|
|
280
|
+
|
|
281
|
+
private scheduleNext(id: string): void {
|
|
282
|
+
const job = this.jobs.get(id);
|
|
283
|
+
if (!job || !job.enabled || !this.started) return;
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
const now = new Date();
|
|
287
|
+
const nextRun = parseCronExpression(job.definition.schedule, now);
|
|
288
|
+
job.nextRunAt = nextRun;
|
|
289
|
+
|
|
290
|
+
const delay = Math.max(nextRun.getTime() - now.getTime(), 0);
|
|
291
|
+
|
|
292
|
+
job.timerId = setTimeout(async () => {
|
|
293
|
+
if (!job.enabled || !this.started) return;
|
|
294
|
+
await this.executeJob(job, false);
|
|
295
|
+
// Schedule the next tick
|
|
296
|
+
this.scheduleNext(id);
|
|
297
|
+
}, delay);
|
|
298
|
+
} catch (err: unknown) {
|
|
299
|
+
console.error(`[cron] Failed to schedule "${id}":`, err);
|
|
300
|
+
job.state = "error";
|
|
301
|
+
job.lastError = err instanceof Error ? err.message : String(err);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private stopJob(id: string): void {
|
|
306
|
+
const job = this.jobs.get(id);
|
|
307
|
+
if (job?.timerId) {
|
|
308
|
+
clearTimeout(job.timerId);
|
|
309
|
+
job.timerId = undefined;
|
|
310
|
+
job.nextRunAt = undefined;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private async executeJob(
|
|
315
|
+
job: RegisteredJob,
|
|
316
|
+
manual: boolean
|
|
317
|
+
): Promise<CronJobLogEntry> {
|
|
318
|
+
const startedAt = new Date();
|
|
319
|
+
const capturedLogs: string[] = [];
|
|
320
|
+
|
|
321
|
+
const ctx: CronJobContext = {
|
|
322
|
+
jobId: job.id,
|
|
323
|
+
scheduledAt: startedAt,
|
|
324
|
+
log: (...args: unknown[]) => {
|
|
325
|
+
const line = args.map((a) =>
|
|
326
|
+
typeof a === "string" ? a : JSON.stringify(a)
|
|
327
|
+
).join(" ");
|
|
328
|
+
capturedLogs.push(line);
|
|
329
|
+
},
|
|
330
|
+
client: this.client!
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
job.state = "running";
|
|
334
|
+
job.lastRunAt = startedAt;
|
|
335
|
+
job.totalRuns++;
|
|
336
|
+
|
|
337
|
+
let success = true;
|
|
338
|
+
let error: string | undefined;
|
|
339
|
+
let result: unknown;
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
// Race with timeout
|
|
343
|
+
const timeout = (job.definition.timeoutSeconds ?? 300) * 1000;
|
|
344
|
+
const handlerPromise = Promise.resolve(job.definition.handler(ctx));
|
|
345
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
346
|
+
setTimeout(() => reject(new Error(`Cron job "${job.id}" timed out after ${timeout}ms`)), timeout);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
result = await Promise.race([handlerPromise, timeoutPromise]);
|
|
350
|
+
} catch (err: unknown) {
|
|
351
|
+
success = false;
|
|
352
|
+
error = err instanceof Error ? err.message : String(err);
|
|
353
|
+
job.totalFailures++;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const finishedAt = new Date();
|
|
357
|
+
const durationMs = finishedAt.getTime() - startedAt.getTime();
|
|
358
|
+
|
|
359
|
+
job.state = success ? (job.enabled ? "idle" : "disabled") : "error";
|
|
360
|
+
job.lastDurationMs = durationMs;
|
|
361
|
+
job.lastError = error;
|
|
362
|
+
|
|
363
|
+
const logEntry: CronJobLogEntry = {
|
|
364
|
+
jobId: job.id,
|
|
365
|
+
startedAt: startedAt.toISOString(),
|
|
366
|
+
finishedAt: finishedAt.toISOString(),
|
|
367
|
+
durationMs,
|
|
368
|
+
success,
|
|
369
|
+
error,
|
|
370
|
+
result: result !== undefined ? result : undefined,
|
|
371
|
+
logs: capturedLogs,
|
|
372
|
+
manual
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// Push to ring buffer
|
|
376
|
+
job.logs.push(logEntry);
|
|
377
|
+
if (job.logs.length > MAX_LOGS_PER_JOB) {
|
|
378
|
+
job.logs.shift();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Persist to database (non-blocking)
|
|
382
|
+
if (this.store) {
|
|
383
|
+
this.store.insertLog(logEntry).catch((err) => {
|
|
384
|
+
console.error(`[cron] Failed to persist log for "${job.id}":`, err);
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (success) {
|
|
389
|
+
console.log(`✅ [cron] "${job.id}" completed in ${durationMs}ms`);
|
|
390
|
+
} else {
|
|
391
|
+
console.error(`❌ [cron] "${job.id}" failed in ${durationMs}ms: ${error}`);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return logEntry;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private toStatus(job: RegisteredJob): CronJobStatus {
|
|
398
|
+
return {
|
|
399
|
+
id: job.id,
|
|
400
|
+
name: job.definition.name,
|
|
401
|
+
description: job.definition.description,
|
|
402
|
+
schedule: job.definition.schedule,
|
|
403
|
+
enabled: job.enabled,
|
|
404
|
+
state: job.state,
|
|
405
|
+
lastRunAt: job.lastRunAt?.toISOString(),
|
|
406
|
+
nextRunAt: job.nextRunAt?.toISOString(),
|
|
407
|
+
lastDurationMs: job.lastDurationMs,
|
|
408
|
+
lastError: job.lastError,
|
|
409
|
+
totalRuns: job.totalRuns,
|
|
410
|
+
totalFailures: job.totalFailures
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import type { CronJobLogEntry } from "@rebasepro/types";
|
|
2
|
+
import type { DataDriver } from "@rebasepro/types";
|
|
3
|
+
import { isSQLAdmin } from "@rebasepro/types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Persistence layer for cron job execution logs.
|
|
7
|
+
*
|
|
8
|
+
* Uses the DataDriver's `admin.executeSql` capability to store logs in a
|
|
9
|
+
* `rebase.cron_logs` table. Falls back gracefully if the driver doesn't
|
|
10
|
+
* support SQL (e.g. MongoDB) — in that case, no persistence occurs.
|
|
11
|
+
*/
|
|
12
|
+
export interface CronStore {
|
|
13
|
+
/** Ensure the backing table exists. Called once on startup. */
|
|
14
|
+
ensureTable(): Promise<void>;
|
|
15
|
+
|
|
16
|
+
/** Persist a single log entry after execution. */
|
|
17
|
+
insertLog(entry: CronJobLogEntry): Promise<void>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Fetch the most recent logs for a job.
|
|
21
|
+
* @param jobId The job identifier
|
|
22
|
+
* @param limit Max entries to return (default 50)
|
|
23
|
+
* @returns Logs sorted newest-first
|
|
24
|
+
*/
|
|
25
|
+
fetchLogs(jobId: string, limit?: number): Promise<CronJobLogEntry[]>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Fetch aggregate stats for all jobs (totalRuns, totalFailures, lastRunAt).
|
|
29
|
+
* Used to seed in-memory counters on startup.
|
|
30
|
+
*/
|
|
31
|
+
fetchJobStats(): Promise<Map<string, { totalRuns: number; totalFailures: number; lastRunAt?: string }>>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ─── SQL-based implementation ────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
const TABLE = "rebase.cron_logs";
|
|
37
|
+
|
|
38
|
+
export function createCronStore(driver: DataDriver): CronStore | undefined {
|
|
39
|
+
const admin = driver.admin;
|
|
40
|
+
if (!isSQLAdmin(admin)) {
|
|
41
|
+
console.warn("⚠️ [cron-store] DataDriver does not support SQL admin — cron logs will not be persisted.");
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const exec = admin.executeSql.bind(admin);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
async ensureTable(): Promise<void> {
|
|
49
|
+
try {
|
|
50
|
+
await exec("CREATE SCHEMA IF NOT EXISTS rebase");
|
|
51
|
+
await exec(`
|
|
52
|
+
CREATE TABLE IF NOT EXISTS ${TABLE} (
|
|
53
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
54
|
+
job_id TEXT NOT NULL,
|
|
55
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
56
|
+
finished_at TIMESTAMPTZ NOT NULL,
|
|
57
|
+
duration_ms INTEGER NOT NULL,
|
|
58
|
+
success BOOLEAN NOT NULL DEFAULT true,
|
|
59
|
+
error TEXT,
|
|
60
|
+
result JSONB,
|
|
61
|
+
logs JSONB,
|
|
62
|
+
manual BOOLEAN NOT NULL DEFAULT false
|
|
63
|
+
)
|
|
64
|
+
`);
|
|
65
|
+
|
|
66
|
+
await exec(`
|
|
67
|
+
CREATE INDEX IF NOT EXISTS idx_cron_logs_job
|
|
68
|
+
ON ${TABLE}(job_id, started_at DESC)
|
|
69
|
+
`);
|
|
70
|
+
|
|
71
|
+
console.log("✅ Cron logs table ready");
|
|
72
|
+
} catch (err) {
|
|
73
|
+
console.error("❌ Failed to create cron logs table:", err);
|
|
74
|
+
console.warn("⚠️ Continuing without cron log persistence.");
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
async insertLog(entry: CronJobLogEntry): Promise<void> {
|
|
79
|
+
try {
|
|
80
|
+
const resultJson = entry.result !== undefined ? JSON.stringify(entry.result) : null;
|
|
81
|
+
const logsJson = entry.logs.length > 0 ? JSON.stringify(entry.logs) : null;
|
|
82
|
+
const errorEscaped = entry.error ? entry.error.replace(/'/g, "''") : null;
|
|
83
|
+
|
|
84
|
+
await exec(`
|
|
85
|
+
INSERT INTO ${TABLE} (job_id, started_at, finished_at, duration_ms, success, error, result, logs, manual)
|
|
86
|
+
VALUES (
|
|
87
|
+
'${entry.jobId}',
|
|
88
|
+
'${entry.startedAt}',
|
|
89
|
+
'${entry.finishedAt}',
|
|
90
|
+
${entry.durationMs},
|
|
91
|
+
${entry.success},
|
|
92
|
+
${errorEscaped ? `'${errorEscaped}'` : "NULL"},
|
|
93
|
+
${resultJson ? `'${resultJson.replace(/'/g, "''")}'::jsonb` : "NULL"},
|
|
94
|
+
${logsJson ? `'${logsJson.replace(/'/g, "''")}'::jsonb` : "NULL"},
|
|
95
|
+
${entry.manual}
|
|
96
|
+
)
|
|
97
|
+
`);
|
|
98
|
+
} catch (err) {
|
|
99
|
+
// Non-blocking — log persistence should never crash the scheduler
|
|
100
|
+
console.error(`[cron-store] Failed to persist log for "${entry.jobId}":`, err);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
async fetchLogs(jobId: string, limit = 50): Promise<CronJobLogEntry[]> {
|
|
105
|
+
try {
|
|
106
|
+
const rows = await exec(`
|
|
107
|
+
SELECT job_id, started_at, finished_at, duration_ms, success, error, result, logs, manual
|
|
108
|
+
FROM ${TABLE}
|
|
109
|
+
WHERE job_id = '${jobId}'
|
|
110
|
+
ORDER BY started_at DESC
|
|
111
|
+
LIMIT ${limit}
|
|
112
|
+
`);
|
|
113
|
+
|
|
114
|
+
return rows.map(rowToLogEntry);
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error(`[cron-store] Failed to fetch logs for "${jobId}":`, err);
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
async fetchJobStats(): Promise<Map<string, { totalRuns: number; totalFailures: number; lastRunAt?: string }>> {
|
|
122
|
+
const stats = new Map<string, { totalRuns: number; totalFailures: number; lastRunAt?: string }>();
|
|
123
|
+
try {
|
|
124
|
+
const rows = await exec(`
|
|
125
|
+
SELECT
|
|
126
|
+
job_id,
|
|
127
|
+
COUNT(*)::int AS total_runs,
|
|
128
|
+
COUNT(*) FILTER (WHERE NOT success)::int AS total_failures,
|
|
129
|
+
MAX(started_at) AS last_run_at
|
|
130
|
+
FROM ${TABLE}
|
|
131
|
+
GROUP BY job_id
|
|
132
|
+
`);
|
|
133
|
+
|
|
134
|
+
for (const row of rows) {
|
|
135
|
+
stats.set(row.job_id as string, {
|
|
136
|
+
totalRuns: row.total_runs as number,
|
|
137
|
+
totalFailures: row.total_failures as number,
|
|
138
|
+
lastRunAt: row.last_run_at ? new Date(row.last_run_at as string).toISOString() : undefined
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error("[cron-store] Failed to fetch job stats:", err);
|
|
143
|
+
}
|
|
144
|
+
return stats;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ─── Helpers ─────────────────────────────────────────────────────────
|
|
150
|
+
|
|
151
|
+
function rowToLogEntry(row: Record<string, unknown>): CronJobLogEntry {
|
|
152
|
+
return {
|
|
153
|
+
jobId: row.job_id as string,
|
|
154
|
+
startedAt: new Date(row.started_at as string).toISOString(),
|
|
155
|
+
finishedAt: new Date(row.finished_at as string).toISOString(),
|
|
156
|
+
durationMs: row.duration_ms as number,
|
|
157
|
+
success: row.success as boolean,
|
|
158
|
+
error: (row.error as string) ?? undefined,
|
|
159
|
+
result: row.result ?? undefined,
|
|
160
|
+
logs: Array.isArray(row.logs) ? row.logs : (row.logs ? JSON.parse(row.logs as string) : []),
|
|
161
|
+
manual: row.manual as boolean
|
|
162
|
+
};
|
|
163
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { loadCronJobsFromDirectory } from "./cron-loader";
|
|
2
|
+
export type { LoadedCronJob } from "./cron-loader";
|
|
3
|
+
export { CronScheduler } from "./cron-scheduler";
|
|
4
|
+
export { createCronRoutes } from "./cron-routes";
|
|
5
|
+
export { createCronStore } from "./cron-store";
|
|
6
|
+
export type { CronStore } from "./cron-store";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Abstraction Interfaces
|
|
3
|
+
*
|
|
4
|
+
* These interfaces define the contracts that any database backend must implement
|
|
5
|
+
* to be used with Rebase. This allows for pluggable database backends like
|
|
6
|
+
* PostgreSQL, MongoDB, MySQL, etc.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
Entity,
|
|
11
|
+
EntityCollection,
|
|
12
|
+
FilterValues,
|
|
13
|
+
WhereFilterOp,
|
|
14
|
+
DatabaseConnection,
|
|
15
|
+
QueryFilter,
|
|
16
|
+
FetchCollectionOptions,
|
|
17
|
+
SearchOptions,
|
|
18
|
+
CountOptions,
|
|
19
|
+
ConditionBuilder,
|
|
20
|
+
ConditionBuilderStatic,
|
|
21
|
+
EntityRepository,
|
|
22
|
+
CollectionSubscriptionConfig,
|
|
23
|
+
EntitySubscriptionConfig,
|
|
24
|
+
RealtimeProvider,
|
|
25
|
+
CollectionRegistryInterface,
|
|
26
|
+
DataTransformer,
|
|
27
|
+
BackendConfig,
|
|
28
|
+
BackendInstance,
|
|
29
|
+
BackendFactory
|
|
30
|
+
} from "@rebasepro/types";
|
|
31
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
32
|
+
import { PgTransaction } from "drizzle-orm/pg-core";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Type representing either a direct database connection or a transaction.
|
|
36
|
+
* Used to allow services to operate within a transaction context.
|
|
37
|
+
* Note: `any` is intentional here — it represents a Drizzle client with
|
|
38
|
+
* a dynamic schema, enabling `db.query[tableName]` access without casts.
|
|
39
|
+
*/
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
+
export type DrizzleClient = NodePgDatabase<Record<string, unknown>> | PgTransaction<any, any, any>;
|
|
42
|
+
|
|
43
|
+
export type {
|
|
44
|
+
DatabaseConnection,
|
|
45
|
+
QueryFilter,
|
|
46
|
+
FetchCollectionOptions,
|
|
47
|
+
SearchOptions,
|
|
48
|
+
CountOptions,
|
|
49
|
+
ConditionBuilder,
|
|
50
|
+
ConditionBuilderStatic,
|
|
51
|
+
EntityRepository,
|
|
52
|
+
CollectionSubscriptionConfig,
|
|
53
|
+
EntitySubscriptionConfig,
|
|
54
|
+
RealtimeProvider,
|
|
55
|
+
CollectionRegistryInterface,
|
|
56
|
+
DataTransformer,
|
|
57
|
+
BackendConfig,
|
|
58
|
+
BackendInstance,
|
|
59
|
+
BackendFactory
|
|
60
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Email module exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type {
|
|
6
|
+
EmailService,
|
|
7
|
+
EmailSendOptions,
|
|
8
|
+
SMTPConfig,
|
|
9
|
+
EmailConfig,
|
|
10
|
+
PasswordResetTemplateFunction,
|
|
11
|
+
EmailVerificationTemplateFunction,
|
|
12
|
+
UserInvitationTemplateFunction,
|
|
13
|
+
WelcomeEmailTemplateFunction
|
|
14
|
+
} from "./types";
|
|
15
|
+
|
|
16
|
+
export { SMTPEmailService, createEmailService } from "./smtp-email-service";
|
|
17
|
+
|
|
18
|
+
export { getPasswordResetTemplate, getEmailVerificationTemplate, getUserInvitationTemplate, getWelcomeEmailTemplate } from "./templates";
|