@rebasepro/server 0.25.0 → 0.25.1
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/dist/{GCSStorageController-C7ARrDAp.js → GCSStorageController-CRd9bFrc.js} +3 -3
- package/dist/{GCSStorageController-C7ARrDAp.js.map → GCSStorageController-CRd9bFrc.js.map} +1 -1
- package/dist/{S3StorageController-qevJ0pVE.js → S3StorageController-D00EFa5P.js} +3 -3
- package/dist/{S3StorageController-qevJ0pVE.js.map → S3StorageController-D00EFa5P.js.map} +1 -1
- package/dist/api/closed-database.d.ts +25 -0
- package/dist/{auth-CteGWxsL.js → auth-Dkvo2tTJ.js} +26 -18
- package/dist/auth-Dkvo2tTJ.js.map +1 -0
- package/dist/backend/boot-announcements.d.ts +13 -0
- package/dist/backend/create-backend.d.ts +4 -0
- package/dist/{backup-Dq0lyJ_D.js → backup-B9m3cb3X.js} +7 -7
- package/dist/{backup-Dq0lyJ_D.js.map → backup-B9m3cb3X.js.map} +1 -1
- package/dist/{contract-routes-DiFwiLg2.js → contract-routes-CsFXW7j4.js} +2 -2
- package/dist/{contract-routes-DiFwiLg2.js.map → contract-routes-CsFXW7j4.js.map} +1 -1
- package/dist/{cron-loader-NHqRfKF0.js → cron-loader-Dj6lA4HQ.js} +2 -2
- package/dist/{cron-loader-NHqRfKF0.js.map → cron-loader-Dj6lA4HQ.js.map} +1 -1
- package/dist/{cron-routes-Dhf82HzT.js → cron-routes-CwbLZguO.js} +5 -5
- package/dist/{cron-routes-Dhf82HzT.js.map → cron-routes-CwbLZguO.js.map} +1 -1
- package/dist/{cron-scheduler-D1DT0nw5.js → cron-scheduler-B04ACoUE.js} +2 -2
- package/dist/{cron-scheduler-D1DT0nw5.js.map → cron-scheduler-B04ACoUE.js.map} +1 -1
- package/dist/{cron-store-DEpVstUU.js → cron-store-DkyfuLOH.js} +3 -3
- package/dist/{cron-store-DEpVstUU.js.map → cron-store-DkyfuLOH.js.map} +1 -1
- package/dist/{ddl-bootstrap-BNqi2NZe.js → ddl-bootstrap-Ck20GSIX.js} +2 -2
- package/dist/{ddl-bootstrap-BNqi2NZe.js.map → ddl-bootstrap-Ck20GSIX.js.map} +1 -1
- package/dist/{errors-_ytoGi8A.js → errors-OMWkKSH-.js} +66 -5
- package/dist/errors-OMWkKSH-.js.map +1 -0
- package/dist/{function-loader-DbCSoNJj.js → function-loader-KKDqTcIr.js} +2 -2
- package/dist/{function-loader-DbCSoNJj.js.map → function-loader-KKDqTcIr.js.map} +1 -1
- package/dist/{function-routes-DNV4RUF0.js → function-routes-RxrCHNuM.js} +2 -2
- package/dist/{function-routes-DNV4RUF0.js.map → function-routes-RxrCHNuM.js.map} +1 -1
- package/dist/functions/index.js +18 -1
- package/dist/functions/index.js.map +1 -1
- package/dist/{history-recorder-RZ1Ow4YY.js → history-recorder-B2rkuhuy.js} +2 -2
- package/dist/{history-recorder-RZ1Ow4YY.js.map → history-recorder-B2rkuhuy.js.map} +1 -1
- package/dist/index.es.js +529 -113
- package/dist/index.es.js.map +1 -1
- package/dist/{jobs-8G-_agld.js → jobs-h1WRq3cp.js} +3 -3
- package/dist/{jobs-8G-_agld.js.map → jobs-h1WRq3cp.js.map} +1 -1
- package/dist/{jwt-YhciR93H.js → jwt-D3oFauZI.js} +2 -2
- package/dist/{jwt-YhciR93H.js.map → jwt-D3oFauZI.js.map} +1 -1
- package/dist/{logger-Bt32sHFw.js → logger-CW8pmTS6.js} +35 -3
- package/dist/logger-CW8pmTS6.js.map +1 -0
- package/dist/{logs-routes-CelTiS7C.js → logs-routes-CBt7mMIL.js} +3 -3
- package/dist/{logs-routes-CelTiS7C.js.map → logs-routes-CBt7mMIL.js.map} +1 -1
- package/dist/{openapi-generator-BamY6-ga.js → openapi-generator-BjHr6upl.js} +2 -2
- package/dist/{openapi-generator-BamY6-ga.js.map → openapi-generator-BjHr6upl.js.map} +1 -1
- package/dist/{proxy-BEKbsil6.js → proxy-CH8Jsovg.js} +2 -2
- package/dist/{proxy-BEKbsil6.js.map → proxy-CH8Jsovg.js.map} +1 -1
- package/dist/{query-parser-CGohjyWI.js → query-parser-otFZqJsC.js} +3 -3
- package/dist/{query-parser-CGohjyWI.js.map → query-parser-otFZqJsC.js.map} +1 -1
- package/dist/{request-timeout-CRny5KD0.js → request-timeout-59crS-9u.js} +3 -3
- package/dist/{request-timeout-CRny5KD0.js.map → request-timeout-59crS-9u.js.map} +1 -1
- package/dist/{schema-editor-routes-CnNGCayU.js → schema-editor-routes-Bsut0bCC.js} +2 -2
- package/dist/{schema-editor-routes-CnNGCayU.js.map → schema-editor-routes-Bsut0bCC.js.map} +1 -1
- package/dist/{soft-delete-params-BAgpmBvp.js → soft-delete-params-BmlVwecC.js} +2 -2
- package/dist/{soft-delete-params-BAgpmBvp.js.map → soft-delete-params-BmlVwecC.js.map} +1 -1
- package/dist/{sql-export-routes-BgTJ7K3S.js → sql-export-routes-DmB_uZ4l.js} +3 -3
- package/dist/{sql-export-routes-BgTJ7K3S.js.map → sql-export-routes-DmB_uZ4l.js.map} +1 -1
- package/dist/{upload-validation-I9cCUBIW.js → upload-validation-Bgo626uY.js} +2 -2
- package/dist/{upload-validation-I9cCUBIW.js.map → upload-validation-Bgo626uY.js.map} +1 -1
- package/dist/utils/dev-dependency-command.d.ts +15 -0
- package/dist/utils/dev-port.d.ts +8 -0
- package/dist/utils/logger.d.ts +24 -0
- package/package.json +5 -5
- package/dist/auth-CteGWxsL.js.map +0 -1
- package/dist/errors-_ytoGi8A.js.map +0 -1
- package/dist/logger-Bt32sHFw.js.map +0 -1
package/dist/index.es.js
CHANGED
|
@@ -7,38 +7,38 @@ import { a as MAX_LIST_LIMIT, c as RebaseClientError, l as isUnsupported, n as s
|
|
|
7
7
|
import { C as isFieldOperation, T as EntityRelation, _ as normalizeOrderBy, b as BATCH_REF_KEY, c as collectAllPages, g as OrderBySpecError, l as paginateFind, m as normalizeInclude, p as mergeIncludeSpecs, r as deserializeFilter, t as RESERVED_QUERY_KEYS, u as resolveFindWindow, v as serializeOrderBy, x as FIELD_OPERATORS, y as withRelationReferencesOnly } from "./filter-dialect-O1HK6Nvr.js";
|
|
8
8
|
import { A as DEFAULT_RESOURCE_KEY, C as declaredSubscriptions, D as setTopicRuntime, E as setQueueRuntime, F as resourceKind, G as DEFAULT_DATA_SOURCE_KEY, H as getDeclaredSubcollections, I as resourceKinds, K as getDataSourceCapabilities, L as RLS_JWT_SQL, M as resolveResourceRefs, N as resourceEnvSuffix, O as DEFAULT_STORAGE_SOURCE_KEY, P as resourceKeyOf, R as RLS_ROLES_SQL, S as declaredQueueConsumers, T as resourceToStorageSource, U as isPostgresCollectionConfig, V as getCollectionDataPath, _ as scopesForRoles, c as intersectScopes, et as sortKeyToString, h as scopeGrantsAny, j as buildResourceGraph, k as storageEnvSuffix, m as scopeGrants, p as parseScope, q as ALL_WHERE_FILTER_OPS, s as hasAdminRole, w as resourceToDataSource, y as summarizeScopes, z as RLS_UID_SQL } from "./src-D5Y3N-S4.js";
|
|
9
9
|
import { a as nestCollectionPatchPaths, c as ADMIN_PROPERTY_KEYS, i as isCollectionPatch, n as collectionPatchProblems, r as diffCollections, s as ADMIN_COLLECTION_KEYS, t as applyCollectionPatch } from "./collection_patch-BRu-BvDv.js";
|
|
10
|
-
import { a as softDeleteFieldOf, c as JUNCTION_PIVOT_KEY, i as restoresSoftDeletedRow, l as hasForeignKeyOnTarget, n as buildCollectionSchema, o as buildTenantSecurityRule, s as INCLUDE_COUNT_KEY, t as buildCollectionInputSchema, u as isManyToMany } from "./openapi-generator-
|
|
10
|
+
import { a as softDeleteFieldOf, c as JUNCTION_PIVOT_KEY, i as restoresSoftDeletedRow, l as hasForeignKeyOnTarget, n as buildCollectionSchema, o as buildTenantSecurityRule, s as INCLUDE_COUNT_KEY, t as buildCollectionInputSchema, u as isManyToMany } from "./openapi-generator-BjHr6upl.js";
|
|
11
11
|
import { n as isAnonymousUid, r as policy } from "./policy-PdzX-Opd.js";
|
|
12
12
|
import { n as isSchemaEditingAdmin, t as isSQLAdmin } from "./backend-DTAOsLQc.js";
|
|
13
|
-
import { a as parseEnvBoolean, i as validateCronExpression, t as CronScheduler } from "./cron-scheduler-
|
|
13
|
+
import { a as parseEnvBoolean, i as validateCronExpression, t as CronScheduler } from "./cron-scheduler-B04ACoUE.js";
|
|
14
14
|
import { n as isValidAppHost, r as reservedPrefixFor, t as SCHEMA_VERSION_HEADER } from "./project_manifest-R6y_KHms.js";
|
|
15
|
-
import { n as createContractRoutes, r as computeSchemaVersion } from "./contract-routes-
|
|
16
|
-
import { $ as optionalAuth, $t as raw, A as createAuthLimiters, At as setDefaultAccessControl, B as registerDevEmailSink, Bt as resolveEmailLinkBase, C as createPersonalKeyRoutes, Ct as configureAccess, D as configureUserPicking, Dt as processAccessControl, E as createBuiltinAuthAdapter, Et as keyOwnerResolverFor, F as MemoryRateLimitStore, Ft as supportsRlsScoping, G as validatePasswordStrength, Gt as getMagicLinkTemplate, H as createEmailService, Ht as getEmailChangeTemplate, I as activeDevEmailSink, It as isAccessTokenRevoked, J as createAdapterAuthMiddleware, Jt as getWelcomeEmailTemplate, K as verifyPassword, Kt as getPasswordResetTemplate, L as clearActiveDevEmailSink, Lt as completeUserCreation, M as createDataRateLimiter, Mt as safeCompare, N as createRateLimiter, Nt as SERVICE_IDENTITY, O as isBootstrapWindowOpen, Ot as requireScope, P as setSharedRateLimitStore, Pt as scopeDataDriver, Q as fileTokenAuth, Qt as html, R as createDevEmailSink, Rt as generateSecurePassword, S as createApiKeyRoutes, St as callerScopes, T as createJwksRoutes, Tt as hasScope, U as resolveAuthHooks, Ut as getEmailOtpTemplate, V as SMTPEmailService, Vt as getEmailChangeNoticeTemplate, W as hashPassword, Wt as getEmailVerificationTemplate, X as createRequireAuth, Xt as RawHtml, Y as createAuthMiddleware, Yt as resolveEmailBranding, Z as extractUserFromToken, Zt as escapeHtml, _ as oauthCodeFlowSchema, _t as isApiKeyToken, a as createBitbucketProvider, an as storageObjectPath, at as userFromToken, b as resolveRateLimitStoreKind, bt as accessModelFromCollections, c as createTwitterProvider, ct as isPublicObjectGrant, d as createMicrosoftProvider, dt as storageRequestWildcard, en as httpMethodToOperation, et as publicObjectAuth, f as tryVerifyOidcIdToken, ft as decideImpersonation, g as createGoogleProvider, gt as createTusScopeGuard, h as createLinkedinProvider, ht as createFunctionScopeGuard, i as createSlackProvider, in as isPublicStoragePath, it as requireAuth, j as createDataRateLimitCheck, jt as extractBearerToken, k as DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT, kt as requireScopeByMethod, l as createFacebookProvider, lt as requestedStorageObject, m as createGitHubProvider, mt as createApiKeyPreAuth, n as createCustomAuthAdapter, nn as PUBLIC_STORAGE_PREFIX, nt as refuse$1, o as createGitLabProvider, ot as PUBLIC_OBJECT_PRINCIPAL, p as verifyOidcIdToken, pt as refuseUnhonouredImpersonation, q as adapterRefusal, qt as getUserInvitationTemplate, r as createSpotifyProvider, rn as isPublicStorageKey, rt as requireAdmin, s as createDiscordProvider, st as isDownloadTokenGrant, tn as securityRuleToConditions, tt as queryTokenAuth, u as createAppleProvider, ut as storageObjectOfPath, v as pkceTokenParams, vt as resolveApiKey, w as createApiKeyStore, wt as getAccessModel, x as createSqlRateLimitStore, xt as accessOf, y as providerVerifiedEmail, yt as validateApiKey, z as extractLinks, zt as assertEmailLinkBases } from "./auth-
|
|
17
|
-
import { a as
|
|
18
|
-
import { a as apiHiddenFieldNames, c as fieldKeyForColumn, d as isJunctionBackedRelation, g as toWireKey, h as resolveRelation, l as findRelation, m as resolveCollectionRelations, n as HARD_DELETE_QUERY_PARAM, p as relationDeclaringProperty, r as parseHardDelete, s as restrictedFieldNames, u as getTableName } from "./soft-delete-params-
|
|
15
|
+
import { n as createContractRoutes, r as computeSchemaVersion } from "./contract-routes-CsFXW7j4.js";
|
|
16
|
+
import { $ as optionalAuth, $t as raw, A as createAuthLimiters, At as setDefaultAccessControl, B as registerDevEmailSink, Bt as resolveEmailLinkBase, C as createPersonalKeyRoutes, Ct as configureAccess, D as configureUserPicking, Dt as processAccessControl, E as createBuiltinAuthAdapter, Et as keyOwnerResolverFor, F as MemoryRateLimitStore, Ft as supportsRlsScoping, G as validatePasswordStrength, Gt as getMagicLinkTemplate, H as createEmailService, Ht as getEmailChangeTemplate, I as activeDevEmailSink, It as isAccessTokenRevoked, J as createAdapterAuthMiddleware, Jt as getWelcomeEmailTemplate, K as verifyPassword, Kt as getPasswordResetTemplate, L as clearActiveDevEmailSink, Lt as completeUserCreation, M as createDataRateLimiter, Mt as safeCompare, N as createRateLimiter, Nt as SERVICE_IDENTITY, O as isBootstrapWindowOpen, Ot as requireScope, P as setSharedRateLimitStore, Pt as scopeDataDriver, Q as fileTokenAuth, Qt as html, R as createDevEmailSink, Rt as generateSecurePassword, S as createApiKeyRoutes, St as callerScopes, T as createJwksRoutes, Tt as hasScope, U as resolveAuthHooks, Ut as getEmailOtpTemplate, V as SMTPEmailService, Vt as getEmailChangeNoticeTemplate, W as hashPassword, Wt as getEmailVerificationTemplate, X as createRequireAuth, Xt as RawHtml, Y as createAuthMiddleware, Yt as resolveEmailBranding, Z as extractUserFromToken, Zt as escapeHtml, _ as oauthCodeFlowSchema, _t as isApiKeyToken, a as createBitbucketProvider, an as storageObjectPath, at as userFromToken, b as resolveRateLimitStoreKind, bt as accessModelFromCollections, c as createTwitterProvider, ct as isPublicObjectGrant, d as createMicrosoftProvider, dt as storageRequestWildcard, en as httpMethodToOperation, et as publicObjectAuth, f as tryVerifyOidcIdToken, ft as decideImpersonation, g as createGoogleProvider, gt as createTusScopeGuard, h as createLinkedinProvider, ht as createFunctionScopeGuard, i as createSlackProvider, in as isPublicStoragePath, it as requireAuth, j as createDataRateLimitCheck, jt as extractBearerToken, k as DEFAULT_FUNCTIONS_ANONYMOUS_LIMIT, kt as requireScopeByMethod, l as createFacebookProvider, lt as requestedStorageObject, m as createGitHubProvider, mt as createApiKeyPreAuth, n as createCustomAuthAdapter, nn as PUBLIC_STORAGE_PREFIX, nt as refuse$1, o as createGitLabProvider, ot as PUBLIC_OBJECT_PRINCIPAL, p as verifyOidcIdToken, pt as refuseUnhonouredImpersonation, q as adapterRefusal, qt as getUserInvitationTemplate, r as createSpotifyProvider, rn as isPublicStorageKey, rt as requireAdmin, s as createDiscordProvider, st as isDownloadTokenGrant, tn as securityRuleToConditions, tt as queryTokenAuth, u as createAppleProvider, ut as storageObjectOfPath, v as pkceTokenParams, vt as resolveApiKey, w as createApiKeyStore, wt as getAccessModel, x as createSqlRateLimitStore, xt as accessOf, y as providerVerifiedEmail, yt as validateApiKey, z as extractLinks, zt as assertEmailLinkBases } from "./auth-Dkvo2tTJ.js";
|
|
17
|
+
import { a as logger, c as setLogLevel, d as hostEnv, i as logLevelEnabled, l as setLogOutput, o as rawQueryLoggingEnabled, r as describeCauseChain, s as redactSensitiveText, t as _setLogLineHook } from "./logger-CW8pmTS6.js";
|
|
18
|
+
import { a as apiHiddenFieldNames, c as fieldKeyForColumn, d as isJunctionBackedRelation, g as toWireKey, h as resolveRelation, l as findRelation, m as resolveCollectionRelations, n as HARD_DELETE_QUERY_PARAM, p as relationDeclaringProperty, r as parseHardDelete, s as restrictedFieldNames, u as getTableName } from "./soft-delete-params-BmlVwecC.js";
|
|
19
19
|
import { c as createTransport, d as parseRebaseJson, h as unref, l as defaultCodeForStatus, m as withETag, o as createCollectionClient, p as etagOf, r as SDKQueryBuilder, u as invalidBulkBody } from "./offline-store-BhYXViha-BW5j1We_.js";
|
|
20
20
|
import "./resumable-upload-threshold-CKv-Ry0l.js";
|
|
21
21
|
import { n as revokeInternalTableSql } from "./internal-tables-L2t39RdT.js";
|
|
22
22
|
import { i as warnDeprecated } from "./load-failure-L5hvfDoG.js";
|
|
23
|
-
import { a as resolveListLimitParam, c as assertReadableFields, d as decodeCursor$1, f as reconcileCursorOrder, i as parseQueryOptions, l as requestViewer, n as parseAggregateSelect, o as assertNoHiddenAggregateNames, r as parseGroupBy, s as assertQueryFieldsReadable, t as orderByEntriesToTuples, u as cursorToStartAfter } from "./query-parser-
|
|
24
|
-
import { a as isDataException, i as errorHandler, n as DATABASE_UNAVAILABLE_MESSAGE, o as isDatabaseUnreachable, r as declaredErrorAnswer, s as schemaDriftRemedy, t as ApiError } from "./errors-
|
|
25
|
-
import { C as randomHex, E as sha256Hex, S as constantTimeEqual, T as sha256Bytes, b as normalizePemFromEnv, d as hasAsymmetricSigningKey, g as setDefaultJwtService, h as processJwtService, l as getJwks, m as jwtOf, n as configureJwt, r as createJwtService, y as verifyJwt } from "./jwt-
|
|
23
|
+
import { a as resolveListLimitParam, c as assertReadableFields, d as decodeCursor$1, f as reconcileCursorOrder, i as parseQueryOptions, l as requestViewer, n as parseAggregateSelect, o as assertNoHiddenAggregateNames, r as parseGroupBy, s as assertQueryFieldsReadable, t as orderByEntriesToTuples, u as cursorToStartAfter } from "./query-parser-otFZqJsC.js";
|
|
24
|
+
import { a as isDataException, i as errorHandler, n as DATABASE_UNAVAILABLE_MESSAGE, o as isDatabaseUnreachable, r as declaredErrorAnswer, s as schemaDriftRemedy, t as ApiError } from "./errors-OMWkKSH-.js";
|
|
25
|
+
import { C as randomHex, E as sha256Hex, S as constantTimeEqual, T as sha256Bytes, b as normalizePemFromEnv, d as hasAsymmetricSigningKey, g as setDefaultJwtService, h as processJwtService, l as getJwks, m as jwtOf, n as configureJwt, r as createJwtService, y as verifyJwt } from "./jwt-D3oFauZI.js";
|
|
26
26
|
import { a as canonicalStorageId, i as canonicalStorageBucket, n as InvalidStorageBucketError, o as canonicalStorageKey, r as InvalidStorageKeyError, t as InvalidListOptionsError } from "./keys-COzQ2aE8.js";
|
|
27
|
-
import { t as logMiddleware } from "./logs-routes-
|
|
28
|
-
import { a as readStreamFully, c as DOCUMENT_MIME_TYPES, i as fileWebStream, l as IMAGE_MIME_TYPES, r as bytesView, s as DEFAULT_MAX_FILE_SIZE, t as validateUploadedFile } from "./upload-validation-
|
|
29
|
-
import { t as S3StorageController } from "./S3StorageController-
|
|
30
|
-
import { t as GCSStorageController } from "./GCSStorageController-
|
|
31
|
-
import { a as isDuplicateObjectRace, i as isConcurrentDdlRace, n as createDdlBootstrapper, t as CONCURRENT_DDL_SQLSTATES } from "./ddl-bootstrap-
|
|
32
|
-
import { t as createCronStore } from "./cron-store-
|
|
33
|
-
import { i as loadFunctionsWithDiagnostics, n as loadFunctions, r as loadFunctionsFromDirectory } from "./function-loader-
|
|
34
|
-
import { a as getDriver, c as getUser, d as identityResolved, f as isAdmin, i as getApiKey, l as getUserId, m as requireDriver, o as getRequestId, p as isAuthenticated, r as requireRole, s as getRoles, t as createFunctionRoutes, u as hasRole } from "./function-routes-
|
|
35
|
-
import "./proxy-
|
|
36
|
-
import "./request-timeout-
|
|
27
|
+
import { t as logMiddleware } from "./logs-routes-CBt7mMIL.js";
|
|
28
|
+
import { a as readStreamFully, c as DOCUMENT_MIME_TYPES, i as fileWebStream, l as IMAGE_MIME_TYPES, r as bytesView, s as DEFAULT_MAX_FILE_SIZE, t as validateUploadedFile } from "./upload-validation-Bgo626uY.js";
|
|
29
|
+
import { t as S3StorageController } from "./S3StorageController-D00EFa5P.js";
|
|
30
|
+
import { t as GCSStorageController } from "./GCSStorageController-CRd9bFrc.js";
|
|
31
|
+
import { a as isDuplicateObjectRace, i as isConcurrentDdlRace, n as createDdlBootstrapper, t as CONCURRENT_DDL_SQLSTATES } from "./ddl-bootstrap-Ck20GSIX.js";
|
|
32
|
+
import { t as createCronStore } from "./cron-store-DkyfuLOH.js";
|
|
33
|
+
import { i as loadFunctionsWithDiagnostics, n as loadFunctions, r as loadFunctionsFromDirectory } from "./function-loader-KKDqTcIr.js";
|
|
34
|
+
import { a as getDriver, c as getUser, d as identityResolved, f as isAdmin, i as getApiKey, l as getUserId, m as requireDriver, o as getRequestId, p as isAuthenticated, r as requireRole, s as getRoles, t as createFunctionRoutes, u as hasRole } from "./function-routes-RxrCHNuM.js";
|
|
35
|
+
import "./proxy-CH8Jsovg.js";
|
|
36
|
+
import "./request-timeout-59crS-9u.js";
|
|
37
37
|
import { t as FunctionSelectionError } from "./selection-CRpqKUbt.js";
|
|
38
|
-
import { a as loadCrons, i as loadCronJobsWithDiagnostics, n as declareCrons, r as loadCronJobsFromDirectory } from "./cron-loader-
|
|
39
|
-
import { t as createCronRoutes } from "./cron-routes-
|
|
40
|
-
import { a as listBackupObjects, c as readBackupBytes, i as backupStorageFromEnv, n as createBackupLinkRoutes, o as parseBackupDestination, r as createBackupRoutes, s as parseBackupTimestamp } from "./backup-
|
|
41
|
-
import { a as defaultBackoff, i as createJobQueue, n as createJobStore, o as currentAmbientTransaction, r as PermanentJobError } from "./jobs-
|
|
38
|
+
import { a as loadCrons, i as loadCronJobsWithDiagnostics, n as declareCrons, r as loadCronJobsFromDirectory } from "./cron-loader-Dj6lA4HQ.js";
|
|
39
|
+
import { t as createCronRoutes } from "./cron-routes-CwbLZguO.js";
|
|
40
|
+
import { a as listBackupObjects, c as readBackupBytes, i as backupStorageFromEnv, n as createBackupLinkRoutes, o as parseBackupDestination, r as createBackupRoutes, s as parseBackupTimestamp } from "./backup-B9m3cb3X.js";
|
|
41
|
+
import { a as defaultBackoff, i as createJobQueue, n as createJobStore, o as currentAmbientTransaction, r as PermanentJobError } from "./jobs-h1WRq3cp.js";
|
|
42
42
|
import { t as METRIC_SERIES } from "./history-store-DPofGVDp.js";
|
|
43
43
|
import { createHash, createSign, randomBytes } from "node:crypto";
|
|
44
44
|
import * as path$2 from "node:path";
|
|
@@ -57,17 +57,17 @@ import { createHmac, randomUUID as randomUUID$1 } from "crypto";
|
|
|
57
57
|
import { createMiddleware } from "hono/factory";
|
|
58
58
|
import { z, z as z$1 } from "zod";
|
|
59
59
|
import { routePath } from "hono/route";
|
|
60
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
61
|
+
import fs$1, { createWriteStream } from "node:fs";
|
|
60
62
|
import { csrf } from "hono/csrf";
|
|
61
63
|
import { COMPRESSIBLE_CONTENT_TYPE_REGEX, compress } from "hono/compress";
|
|
62
64
|
import { pipeline } from "stream/promises";
|
|
63
|
-
import fs$1, { createWriteStream } from "node:fs";
|
|
64
65
|
import * as fs$2 from "node:fs/promises";
|
|
65
66
|
import fsp from "node:fs/promises";
|
|
66
67
|
import os from "node:os";
|
|
67
68
|
import { mkdir, open, readFile, readdir, stat, unlink, writeFile } from "fs/promises";
|
|
68
69
|
import { execFile } from "node:child_process";
|
|
69
70
|
import { promisify as promisify$1 } from "node:util";
|
|
70
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
71
71
|
import { env, getRuntimeKey } from "hono/adapter";
|
|
72
72
|
import { sql } from "drizzle-orm";
|
|
73
73
|
import net from "net";
|
|
@@ -6617,6 +6617,37 @@ function realtimeLostError() {
|
|
|
6617
6617
|
function realtimeDebugOn() {
|
|
6618
6618
|
return Reflect.get(globalThis, "__REBASE_DEBUG_REALTIME__") === true;
|
|
6619
6619
|
}
|
|
6620
|
+
/**
|
|
6621
|
+
* Connection chatter: the socket opened, signed in, closed, is redialling.
|
|
6622
|
+
*
|
|
6623
|
+
* `console.debug` where that is a debug level — a browser's console hides it
|
|
6624
|
+
* unless Verbose is asked for. Not in Node, where `console.debug` is
|
|
6625
|
+
* `console.log`: a script's `listen()` opened its stdout with "Connected to
|
|
6626
|
+
* PostgreSQL backend" and "WebSocket auto-authenticated", ahead of what the
|
|
6627
|
+
* script prints. There it is written only while realtime debugging is on.
|
|
6628
|
+
*/
|
|
6629
|
+
function connectionDebug(...args) {
|
|
6630
|
+
if (!(typeof window !== "undefined" || typeof document !== "undefined") && !realtimeDebugOn()) return;
|
|
6631
|
+
console.debug(...args);
|
|
6632
|
+
}
|
|
6633
|
+
/**
|
|
6634
|
+
* The codes an `AUTHENTICATE` is refused with when the server will not honour
|
|
6635
|
+
* the session: the token does not verify or has expired, the session was
|
|
6636
|
+
* revoked or its account is gone (`SESSION_REVOKED`), the account is disabled
|
|
6637
|
+
* (`ACCOUNT_DISABLED`) — the codes the HTTP routes answer the same token with.
|
|
6638
|
+
* An answer, not a failure: asking again with the same token gets the same one.
|
|
6639
|
+
*/
|
|
6640
|
+
var SESSION_REFUSAL_CODES = /* @__PURE__ */ new Set([
|
|
6641
|
+
"INVALID_TOKEN",
|
|
6642
|
+
"TOKEN_EXPIRED",
|
|
6643
|
+
"JWT_EXPIRED",
|
|
6644
|
+
"SESSION_REVOKED",
|
|
6645
|
+
"ACCOUNT_DISABLED",
|
|
6646
|
+
"SESSION_ENDED"
|
|
6647
|
+
]);
|
|
6648
|
+
function isSessionRefusal(error) {
|
|
6649
|
+
return error instanceof RebaseApiError && typeof error.code === "string" && SESSION_REFUSAL_CODES.has(error.code);
|
|
6650
|
+
}
|
|
6620
6651
|
/** The ceiling of the reconnect backoff. */
|
|
6621
6652
|
var MAX_RECONNECT_DELAY_MS = 3e4;
|
|
6622
6653
|
/**
|
|
@@ -6962,7 +6993,7 @@ var RebaseWebSocketClient = class {
|
|
|
6962
6993
|
if (this.closedByCaller) return;
|
|
6963
6994
|
if (this.connectionState !== "reconnecting" && this.connectionState !== "disconnected") return;
|
|
6964
6995
|
if (this.ws) return;
|
|
6965
|
-
|
|
6996
|
+
connectionDebug(`${reason} — retrying the realtime connection`);
|
|
6966
6997
|
if (this.reconnectTimeout) {
|
|
6967
6998
|
clearTimeout(this.reconnectTimeout);
|
|
6968
6999
|
this.reconnectTimeout = null;
|
|
@@ -6994,6 +7025,24 @@ var RebaseWebSocketClient = class {
|
|
|
6994
7025
|
}
|
|
6995
7026
|
onlineListener = null;
|
|
6996
7027
|
visibilityListener = null;
|
|
7028
|
+
/** The session this socket's server refused, and why. Cleared when a socket opens. */
|
|
7029
|
+
refusedSession = null;
|
|
7030
|
+
/**
|
|
7031
|
+
* Whether `error` is the server refusing the session `token` signs in as —
|
|
7032
|
+
* revoked, its account gone or disabled, a token that does not verify. Said
|
|
7033
|
+
* once per socket and token, as a warning that names the code.
|
|
7034
|
+
*/
|
|
7035
|
+
noteSessionRefused(token, error) {
|
|
7036
|
+
if (!isSessionRefusal(error)) return false;
|
|
7037
|
+
if (this.refusedSession?.token !== token) {
|
|
7038
|
+
this.refusedSession = {
|
|
7039
|
+
token,
|
|
7040
|
+
error
|
|
7041
|
+
};
|
|
7042
|
+
console.warn(`[Rebase] Realtime: the server refused this session (${error.code}: ${error.message}). The socket is connected but not signed in, and what needs an account is refused with that code.`);
|
|
7043
|
+
}
|
|
7044
|
+
return true;
|
|
7045
|
+
}
|
|
6997
7046
|
/**
|
|
6998
7047
|
* Authenticate the WebSocket connection
|
|
6999
7048
|
*/
|
|
@@ -7042,10 +7091,10 @@ var RebaseWebSocketClient = class {
|
|
|
7042
7091
|
if (this.isConnected && !this.isAuthenticated && !this.authPromise) this.getAuthToken().then((token) => {
|
|
7043
7092
|
if (!this.ws) return;
|
|
7044
7093
|
if (token) this.authenticate(token).catch((e) => {
|
|
7045
|
-
if (this.ws)
|
|
7094
|
+
if (this.ws) connectionDebug("WebSocket auto-auth skipped:", e?.message || e);
|
|
7046
7095
|
});
|
|
7047
7096
|
}).catch((e) => {
|
|
7048
|
-
if (this.ws)
|
|
7097
|
+
if (this.ws) connectionDebug("WebSocket auto-auth skipped:", e?.message || e);
|
|
7049
7098
|
});
|
|
7050
7099
|
}
|
|
7051
7100
|
/**
|
|
@@ -7061,6 +7110,7 @@ var RebaseWebSocketClient = class {
|
|
|
7061
7110
|
if (permanent) this.removeNetworkListeners();
|
|
7062
7111
|
this.isAuthenticated = false;
|
|
7063
7112
|
this.authPromise = null;
|
|
7113
|
+
this.refusedSession = null;
|
|
7064
7114
|
if (this.reconnectTimeout) {
|
|
7065
7115
|
clearTimeout(this.reconnectTimeout);
|
|
7066
7116
|
this.reconnectTimeout = null;
|
|
@@ -7095,7 +7145,7 @@ var RebaseWebSocketClient = class {
|
|
|
7095
7145
|
const socket = new this.WebSocketConstructor(withOptIns(this.websocketUrl));
|
|
7096
7146
|
this.ws = socket;
|
|
7097
7147
|
this.ws.onopen = async () => {
|
|
7098
|
-
|
|
7148
|
+
connectionDebug("Connected to PostgreSQL backend");
|
|
7099
7149
|
const wasReconnect = this.hadConnection;
|
|
7100
7150
|
this.hadConnection = true;
|
|
7101
7151
|
this.isConnected = true;
|
|
@@ -7104,14 +7154,18 @@ var RebaseWebSocketClient = class {
|
|
|
7104
7154
|
this.outageReported = false;
|
|
7105
7155
|
this.gaveUp = false;
|
|
7106
7156
|
this.setState("connected");
|
|
7107
|
-
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
|
|
7111
|
-
|
|
7157
|
+
this.refusedSession = null;
|
|
7158
|
+
if (this.getAuthToken && !this.isAuthenticated) {
|
|
7159
|
+
let token = null;
|
|
7160
|
+
try {
|
|
7161
|
+
token = await this.getAuthToken();
|
|
7162
|
+
if (token) {
|
|
7163
|
+
await this.authenticate(token);
|
|
7164
|
+
connectionDebug("WebSocket auto-authenticated");
|
|
7165
|
+
}
|
|
7166
|
+
} catch (error) {
|
|
7167
|
+
if (!(token && this.noteSessionRefused(token, error))) connectionDebug("WebSocket connected, not signed in yet:", error?.message || error);
|
|
7112
7168
|
}
|
|
7113
|
-
} catch (error) {
|
|
7114
|
-
console.debug("WebSocket connected without auth:", error?.message || error);
|
|
7115
7169
|
}
|
|
7116
7170
|
this.emit(wasReconnect ? "reconnect" : "connect");
|
|
7117
7171
|
this.processMessageQueue(wasReconnect);
|
|
@@ -7128,7 +7182,7 @@ var RebaseWebSocketClient = class {
|
|
|
7128
7182
|
}
|
|
7129
7183
|
};
|
|
7130
7184
|
const onClosed = () => {
|
|
7131
|
-
|
|
7185
|
+
connectionDebug("Disconnected from PostgreSQL backend");
|
|
7132
7186
|
this.stopLivenessWatch();
|
|
7133
7187
|
this.forgetServerOptIns();
|
|
7134
7188
|
if (this.ws === socket) this.ws = null;
|
|
@@ -7258,7 +7312,7 @@ var RebaseWebSocketClient = class {
|
|
|
7258
7312
|
this.reconnectAttempts = Math.min(this.reconnectAttempts + 1, 30);
|
|
7259
7313
|
const ceiling = Math.min(1e3 * Math.pow(2, this.reconnectAttempts), MAX_RECONNECT_DELAY_MS);
|
|
7260
7314
|
const delay = MIN_RECONNECT_DELAY_MS + Math.floor(Math.random() * (ceiling - MIN_RECONNECT_DELAY_MS));
|
|
7261
|
-
|
|
7315
|
+
connectionDebug(`Attempting to reconnect in ${delay}ms (attempt ${this.reconnectAttempts})`);
|
|
7262
7316
|
if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
|
|
7263
7317
|
this.reconnectTimeout = setTimeout(() => {
|
|
7264
7318
|
this.reconnectTimeout = null;
|
|
@@ -7527,31 +7581,36 @@ var RebaseWebSocketClient = class {
|
|
|
7527
7581
|
}
|
|
7528
7582
|
async runAuthentication(retryCount) {
|
|
7529
7583
|
let lastError = null;
|
|
7530
|
-
for (let attempt = 0; attempt < retryCount; attempt++)
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
throw error;
|
|
7542
|
-
|
|
7543
|
-
|
|
7584
|
+
for (let attempt = 0; attempt < retryCount; attempt++) {
|
|
7585
|
+
let token = null;
|
|
7586
|
+
try {
|
|
7587
|
+
token = await this.getAuthToken();
|
|
7588
|
+
if (!token) throw new Error("user not logged in");
|
|
7589
|
+
if (this.refusedSession?.token === token) throw this.refusedSession.error;
|
|
7590
|
+
await this.authenticate(token);
|
|
7591
|
+
connectionDebug("WebSocket authenticated on demand");
|
|
7592
|
+
return;
|
|
7593
|
+
} catch (error) {
|
|
7594
|
+
lastError = error;
|
|
7595
|
+
if (token && this.noteSessionRefused(token, error)) throw error;
|
|
7596
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
7597
|
+
if (errMsg.includes("not logged in") || errMsg.includes("Session expired")) {
|
|
7598
|
+
console.warn("WebSocket auth failed: user not logged in");
|
|
7599
|
+
throw error;
|
|
7600
|
+
}
|
|
7601
|
+
if (errMsg.includes("still loading")) {
|
|
7602
|
+
if (attempt < retryCount - 1) {
|
|
7603
|
+
const delay = Math.min(500 * (attempt + 1), 2e3);
|
|
7604
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
7605
|
+
continue;
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7544
7608
|
if (attempt < retryCount - 1) {
|
|
7545
|
-
const delay = Math.min(
|
|
7609
|
+
const delay = Math.min(1e3 * (attempt + 1), 3e3);
|
|
7610
|
+
connectionDebug(`WebSocket auth attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
|
|
7546
7611
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
7547
|
-
continue;
|
|
7548
7612
|
}
|
|
7549
7613
|
}
|
|
7550
|
-
if (attempt < retryCount - 1) {
|
|
7551
|
-
const delay = Math.min(1e3 * (attempt + 1), 3e3);
|
|
7552
|
-
console.debug(`WebSocket auth attempt ${attempt + 1} failed, retrying in ${delay}ms...`);
|
|
7553
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
7554
|
-
}
|
|
7555
7614
|
}
|
|
7556
7615
|
console.warn("WebSocket on-demand auth failed after retries:", lastError);
|
|
7557
7616
|
throw lastError;
|
|
@@ -7563,7 +7622,7 @@ var RebaseWebSocketClient = class {
|
|
|
7563
7622
|
const token = await this.getAuthToken();
|
|
7564
7623
|
if (!token) throw new Error("user not logged in");
|
|
7565
7624
|
await this.authenticate(token);
|
|
7566
|
-
|
|
7625
|
+
connectionDebug("WebSocket reauthenticated successfully");
|
|
7567
7626
|
} catch (error) {
|
|
7568
7627
|
console.error("WebSocket reauthentication failed:", error);
|
|
7569
7628
|
throw error;
|
|
@@ -7637,6 +7696,10 @@ var RebaseWebSocketClient = class {
|
|
|
7637
7696
|
await this.ensureAuthenticated();
|
|
7638
7697
|
} catch (error) {
|
|
7639
7698
|
if (!(isSubscriptionFrame && isNotSignedIn(error))) {
|
|
7699
|
+
if (error instanceof RebaseApiError) {
|
|
7700
|
+
reject(error);
|
|
7701
|
+
return;
|
|
7702
|
+
}
|
|
7640
7703
|
reject(new RebaseApiError(error instanceof Error ? error.message : "Authentication required"));
|
|
7641
7704
|
return;
|
|
7642
7705
|
}
|
|
@@ -8249,7 +8312,7 @@ var RebaseWebSocketClient = class {
|
|
|
8249
8312
|
* we need to re-register everything to resume receiving updates.
|
|
8250
8313
|
*/
|
|
8251
8314
|
resubscribeAll() {
|
|
8252
|
-
|
|
8315
|
+
connectionDebug(`[WS] Re-subscribing: ${this.collectionSubscriptions.size} collection(s), ${this.singleSubscriptions.size} row(ies)`);
|
|
8253
8316
|
for (const [key, sub] of this.collectionSubscriptions.entries()) {
|
|
8254
8317
|
if (sub.subscribeInFlight) continue;
|
|
8255
8318
|
const oldBackendId = sub.backendSubscriptionId;
|
|
@@ -14802,6 +14865,99 @@ function recordMountPath() {
|
|
|
14802
14865
|
};
|
|
14803
14866
|
}
|
|
14804
14867
|
//#endregion
|
|
14868
|
+
//#region src/backend/boot-announcements.ts
|
|
14869
|
+
/**
|
|
14870
|
+
* What a backend says while it is built, held until it serves.
|
|
14871
|
+
*
|
|
14872
|
+
* `createRebaseBackend` builds everything and mounts nothing, and its build
|
|
14873
|
+
* logs the boot: the connection's posture, change capture, the per-boot key,
|
|
14874
|
+
* which process owns the schema. Those lines are the server's to print — a
|
|
14875
|
+
* server mounts the backend a moment later and they are its boot log. A
|
|
14876
|
+
* script builds a backend to call `backend.rebase` or `backend.fetch` and
|
|
14877
|
+
* never mounts it, and there the same lines were the first thing on its
|
|
14878
|
+
* stdout, ahead of the JSON it exists to print (`tsx script.ts > out.json`).
|
|
14879
|
+
*
|
|
14880
|
+
* So the info lines said inside the build — by this package or a driver, in
|
|
14881
|
+
* the build's async context — are held, and written when the backend first
|
|
14882
|
+
* serves: `mount()`, a route group, `attachRealtime()` or `start()`. They come
|
|
14883
|
+
* out in the order they were said, with the time they were said, before
|
|
14884
|
+
* anything the mount says itself; a server's boot reads as it did. A backend
|
|
14885
|
+
* that never serves never writes them, and what it logs at info afterwards —
|
|
14886
|
+
* the request log of `backend.fetch`, its shutdown — is not written either:
|
|
14887
|
+
* that is a library talking to itself.
|
|
14888
|
+
*
|
|
14889
|
+
* Not held, ever:
|
|
14890
|
+
* - warnings and errors, which say something is wrong and are written at once;
|
|
14891
|
+
* - anything at `LOG_LEVEL=debug`, which asks for every line as it happens;
|
|
14892
|
+
* - a line from anywhere outside the build — the application's own, another
|
|
14893
|
+
* backend's, a request a served backend answers.
|
|
14894
|
+
*
|
|
14895
|
+
* A build that throws writes what it held: the lines before a failure are the
|
|
14896
|
+
* failure's context.
|
|
14897
|
+
*
|
|
14898
|
+
* Host-side: the build's context is an `AsyncLocalStorage`, so this module is
|
|
14899
|
+
* never reachable from `@rebasepro/server/functions`. The logger asks it
|
|
14900
|
+
* through `LogLineHook`, which needs nothing from Node.
|
|
14901
|
+
*
|
|
14902
|
+
* @module
|
|
14903
|
+
*/
|
|
14904
|
+
/**
|
|
14905
|
+
* The most lines one build holds. A build says a few dozen; past this they are
|
|
14906
|
+
* written as they come rather than kept, so a pathological build cannot grow
|
|
14907
|
+
* the buffer without bound.
|
|
14908
|
+
*/
|
|
14909
|
+
var MAX_HELD_LINES = 500;
|
|
14910
|
+
var context = new AsyncLocalStorage();
|
|
14911
|
+
var installed = false;
|
|
14912
|
+
/**
|
|
14913
|
+
* Point the logger at the build contexts. Once per copy of this module; a hook
|
|
14914
|
+
* another copy installed first is asked about every line that is not ours.
|
|
14915
|
+
*/
|
|
14916
|
+
function installHook() {
|
|
14917
|
+
if (installed) return;
|
|
14918
|
+
installed = true;
|
|
14919
|
+
const previous = _setLogLineHook((level, write) => {
|
|
14920
|
+
const own = context.getStore();
|
|
14921
|
+
if (own) return own.offer(level, write);
|
|
14922
|
+
return previous ? previous(level, write) : false;
|
|
14923
|
+
});
|
|
14924
|
+
}
|
|
14925
|
+
var Announcements = class {
|
|
14926
|
+
phase = "building";
|
|
14927
|
+
held = [];
|
|
14928
|
+
run(fn) {
|
|
14929
|
+
installHook();
|
|
14930
|
+
return context.run(this, fn);
|
|
14931
|
+
}
|
|
14932
|
+
/** Whether this backend takes the line: holds it, or does not write it. */
|
|
14933
|
+
offer(level, write) {
|
|
14934
|
+
if (this.phase === "serving") return false;
|
|
14935
|
+
if (level === "warn" || level === "error") return false;
|
|
14936
|
+
if (logLevelEnabled("debug")) return false;
|
|
14937
|
+
if (this.phase === "built") return true;
|
|
14938
|
+
if (this.held.length >= MAX_HELD_LINES) return false;
|
|
14939
|
+
this.held.push(write);
|
|
14940
|
+
return true;
|
|
14941
|
+
}
|
|
14942
|
+
built() {
|
|
14943
|
+
if (this.phase === "building") this.phase = "built";
|
|
14944
|
+
}
|
|
14945
|
+
failed() {
|
|
14946
|
+
this.serve();
|
|
14947
|
+
}
|
|
14948
|
+
serve() {
|
|
14949
|
+
if (this.phase === "serving") return;
|
|
14950
|
+
this.phase = "serving";
|
|
14951
|
+
const held = this.held;
|
|
14952
|
+
this.held = [];
|
|
14953
|
+
for (const write of held) write();
|
|
14954
|
+
}
|
|
14955
|
+
};
|
|
14956
|
+
/** Announcements for one backend about to be built. */
|
|
14957
|
+
function createBootAnnouncements() {
|
|
14958
|
+
return new Announcements();
|
|
14959
|
+
}
|
|
14960
|
+
//#endregion
|
|
14805
14961
|
//#region src/utils/byte-size.ts
|
|
14806
14962
|
/**
|
|
14807
14963
|
* A byte count as an operator reads it in an error message: `10MB`, `1.5MB`,
|
|
@@ -14825,6 +14981,93 @@ function trim(value) {
|
|
|
14825
14981
|
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
14826
14982
|
}
|
|
14827
14983
|
//#endregion
|
|
14984
|
+
//#region src/utils/dev-dependency-command.ts
|
|
14985
|
+
/**
|
|
14986
|
+
* "Install this optional dependency", spelled so it works where the reader
|
|
14987
|
+
* types it: from the project root, in the project's own package manager.
|
|
14988
|
+
*
|
|
14989
|
+
* A scaffolded project is a workspace whose backend is the member `backend`,
|
|
14990
|
+
* and the backend runs there. A bare `pnpm add -D <pkg>` typed at the root —
|
|
14991
|
+
* where a reader is, reading `rebase dev`'s output or the admin panel —
|
|
14992
|
+
* refuses with `ERR_PNPM_ADDING_TO_ROOT`, so the command names the member.
|
|
14993
|
+
*
|
|
14994
|
+
* The same rules as the driver's `describeDevAddCommand`
|
|
14995
|
+
* (`@rebasepro/server-postgres`, `cli-helpers.ts`), which the CLI prints for
|
|
14996
|
+
* atlas. That package depends on this one, not the other way round, so the
|
|
14997
|
+
* server keeps its own copy. Host-side: it reads the file system.
|
|
14998
|
+
*
|
|
14999
|
+
* @module
|
|
15000
|
+
*/
|
|
15001
|
+
var LOCKFILES = [
|
|
15002
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
15003
|
+
["bun.lock", "bun"],
|
|
15004
|
+
["bun.lockb", "bun"],
|
|
15005
|
+
["yarn.lock", "yarn"],
|
|
15006
|
+
["package-lock.json", "npm"]
|
|
15007
|
+
];
|
|
15008
|
+
/** Which package manager built the project, read off the nearest lockfile at or above `startDir`. pnpm when none. */
|
|
15009
|
+
function projectPackageManager(startDir) {
|
|
15010
|
+
for (let dir = path.resolve(startDir);; dir = path.dirname(dir)) {
|
|
15011
|
+
for (const [file, manager] of LOCKFILES) if (fs$1.existsSync(path.join(dir, file))) return manager;
|
|
15012
|
+
if (path.dirname(dir) === dir) return "pnpm";
|
|
15013
|
+
}
|
|
15014
|
+
}
|
|
15015
|
+
function isWorkspaceRoot(dir) {
|
|
15016
|
+
if (fs$1.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return true;
|
|
15017
|
+
try {
|
|
15018
|
+
const manifest = JSON.parse(fs$1.readFileSync(path.join(dir, "package.json"), "utf8"));
|
|
15019
|
+
return typeof manifest === "object" && manifest !== null && "workspaces" in manifest;
|
|
15020
|
+
} catch {
|
|
15021
|
+
return false;
|
|
15022
|
+
}
|
|
15023
|
+
}
|
|
15024
|
+
/**
|
|
15025
|
+
* The workspace member `startDir` is in, relative to the workspace root —
|
|
15026
|
+
* `backend` in a scaffold — or null at that root and outside any workspace.
|
|
15027
|
+
*/
|
|
15028
|
+
function workspaceMember(startDir) {
|
|
15029
|
+
let member = path.resolve(startDir);
|
|
15030
|
+
while (!fs$1.existsSync(path.join(member, "package.json"))) {
|
|
15031
|
+
const parent = path.dirname(member);
|
|
15032
|
+
if (parent === member) return null;
|
|
15033
|
+
member = parent;
|
|
15034
|
+
}
|
|
15035
|
+
for (let dir = path.dirname(member);; dir = path.dirname(dir)) {
|
|
15036
|
+
if (isWorkspaceRoot(dir)) return path.relative(dir, member).split(path.sep).join("/");
|
|
15037
|
+
if (path.dirname(dir) === dir) return null;
|
|
15038
|
+
}
|
|
15039
|
+
}
|
|
15040
|
+
/** A path as one shell word: as it is when it needs no quoting, single-quoted otherwise. */
|
|
15041
|
+
var shellWord = (value) => /^[\w./-]+$/.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
15042
|
+
/**
|
|
15043
|
+
* `add -D <spec>` for the package the backend in `startDir` runs from, typed
|
|
15044
|
+
* at the project root: `pnpm --dir backend add -D ts-morph@28.0.0` in a
|
|
15045
|
+
* scaffold, `pnpm add -D ts-morph@28.0.0` where the backend is the project.
|
|
15046
|
+
*/
|
|
15047
|
+
function devDependencyInstallCommand(spec, startDir = process.cwd()) {
|
|
15048
|
+
const member = workspaceMember(startDir);
|
|
15049
|
+
const dir = member === null ? null : shellWord(member);
|
|
15050
|
+
switch (projectPackageManager(startDir)) {
|
|
15051
|
+
case "npm": return dir ? `npm install -D ${spec} -w ${dir}` : `npm install -D ${spec}`;
|
|
15052
|
+
case "yarn": return dir ? `yarn --cwd ${dir} add -D ${spec}` : `yarn add -D ${spec}`;
|
|
15053
|
+
case "bun": return dir ? `bun add -d ${spec} --cwd ${dir}` : `bun add -d ${spec}`;
|
|
15054
|
+
default: return dir ? `pnpm --dir ${dir} add -D ${spec}` : `pnpm add -D ${spec}`;
|
|
15055
|
+
}
|
|
15056
|
+
}
|
|
15057
|
+
/** The `ts-morph` the schema editor is built against: the server's optional peer. */
|
|
15058
|
+
var TS_MORPH = "ts-morph@28.0.0";
|
|
15059
|
+
/**
|
|
15060
|
+
* How to turn on what needs `ts-morph`, as the end of a sentence that has
|
|
15061
|
+
* said it is missing: "Run `pnpm --dir backend add -D ts-morph@28.0.0` from
|
|
15062
|
+
* the project root, then restart the backend, to enable it."
|
|
15063
|
+
*
|
|
15064
|
+
* `pnpm`, never `npm`, in a pnpm project: running npm inside one rewrites
|
|
15065
|
+
* node_modules into a hoisted layout pnpm then disagrees with (docs/bug-classes.md §5).
|
|
15066
|
+
*/
|
|
15067
|
+
function tsMorphInstallSentence(startDir = process.cwd()) {
|
|
15068
|
+
return `Run \`${devDependencyInstallCommand(TS_MORPH, startDir)}\`${workspaceMember(startDir) === null ? "" : " from the project root"}, then restart the backend, to enable it.`;
|
|
15069
|
+
}
|
|
15070
|
+
//#endregion
|
|
14828
15071
|
//#region src/utils/compression.ts
|
|
14829
15072
|
/**
|
|
14830
15073
|
* Below this many bytes a body is sent as it is. Hono's own default: gzip's
|
|
@@ -18442,11 +18685,11 @@ async function createStorageController(config) {
|
|
|
18442
18685
|
switch (config.type) {
|
|
18443
18686
|
case "local": return new LocalStorageController(config);
|
|
18444
18687
|
case "s3": {
|
|
18445
|
-
const { S3StorageController } = await import("./S3StorageController-
|
|
18688
|
+
const { S3StorageController } = await import("./S3StorageController-D00EFa5P.js").then((n) => n.n);
|
|
18446
18689
|
return new S3StorageController(config);
|
|
18447
18690
|
}
|
|
18448
18691
|
case "gcs": {
|
|
18449
|
-
const { GCSStorageController } = await import("./GCSStorageController-
|
|
18692
|
+
const { GCSStorageController } = await import("./GCSStorageController-CRd9bFrc.js").then((n) => n.n);
|
|
18450
18693
|
return new GCSStorageController(config);
|
|
18451
18694
|
}
|
|
18452
18695
|
default: throw new Error(`Unknown storage type: ${config.type}. Built-in types: local, s3, gcs. For other providers, implement the StorageController interface directly.`);
|
|
@@ -18685,7 +18928,7 @@ function noCollectionsCause(unprotected) {
|
|
|
18685
18928
|
* routes wherever and as often as the application asks.
|
|
18686
18929
|
*/
|
|
18687
18930
|
async function loadOpenApiGenerator() {
|
|
18688
|
-
const { generateOpenApiSpec } = await import("./openapi-generator-
|
|
18931
|
+
const { generateOpenApiSpec } = await import("./openapi-generator-BjHr6upl.js").then((n) => n.r);
|
|
18689
18932
|
return generateOpenApiSpec;
|
|
18690
18933
|
}
|
|
18691
18934
|
/**
|
|
@@ -19778,7 +20021,7 @@ function createLiveSchemaRoutes(config) {
|
|
|
19778
20021
|
enabled: false,
|
|
19779
20022
|
canPlan: true,
|
|
19780
20023
|
code: off?.code ?? "SCHEMA_EDITOR_MISSING_DEPENDENCY",
|
|
19781
|
-
reason: off?.message ??
|
|
20024
|
+
reason: off?.message ?? `Rewriting collection source needs \`ts-morph\`, which is not installed on this server. ${tsMorphInstallSentence()}`
|
|
19782
20025
|
});
|
|
19783
20026
|
}
|
|
19784
20027
|
if (!repository) return c.json({
|
|
@@ -25989,8 +26232,23 @@ function resolveProvision(provision) {
|
|
|
25989
26232
|
* which {@link setDefaultBackend} sets and `initializeRebaseBackend` sets for
|
|
25990
26233
|
* its backend. The one process-wide setting it touches is the log level, and
|
|
25991
26234
|
* only when `logging.level` is given: the logger is the process's.
|
|
26235
|
+
*
|
|
26236
|
+
* What the build logs at info is written when the backend first serves — see
|
|
26237
|
+
* `boot-announcements.ts` — so a script that builds one and never mounts it
|
|
26238
|
+
* prints nothing of the boot's on its stdout.
|
|
25992
26239
|
*/
|
|
25993
26240
|
async function createRebaseBackend(options) {
|
|
26241
|
+
const announcements = createBootAnnouncements();
|
|
26242
|
+
try {
|
|
26243
|
+
const backend = await announcements.run(() => buildRebaseBackend(options, announcements));
|
|
26244
|
+
announcements.built();
|
|
26245
|
+
return backend;
|
|
26246
|
+
} catch (error) {
|
|
26247
|
+
announcements.failed();
|
|
26248
|
+
throw error;
|
|
26249
|
+
}
|
|
26250
|
+
}
|
|
26251
|
+
async function buildRebaseBackend(options, announcements) {
|
|
25994
26252
|
const config = options;
|
|
25995
26253
|
const provision = resolveProvision(config.provision);
|
|
25996
26254
|
if (config.logging?.level !== void 0) setLogLevel(config.logging.level);
|
|
@@ -26270,7 +26528,7 @@ async function createRebaseBackend(options) {
|
|
|
26270
26528
|
const platformTokenPreAuth = createPlatformTokenPreAuth(await platformTokensFromEnv(process.env));
|
|
26271
26529
|
let backupLinkRouter;
|
|
26272
26530
|
if (surfaces.admin) {
|
|
26273
|
-
const { createBackupLinkRoutes, backupDestinationFromEnv } = await import("./backup-
|
|
26531
|
+
const { createBackupLinkRoutes, backupDestinationFromEnv } = await import("./backup-B9m3cb3X.js").then((n) => n.t);
|
|
26274
26532
|
backupLinkRouter = createBackupLinkRoutes({ getDestination: () => backupDestinationFromEnv(process.env) });
|
|
26275
26533
|
}
|
|
26276
26534
|
const apiKeyRouter = apiKeyStore && surfaces.admin ? createApiKeyRoutes({
|
|
@@ -26415,7 +26673,7 @@ async function createRebaseBackend(options) {
|
|
|
26415
26673
|
]) {
|
|
26416
26674
|
const providerConfig = safeAuthConfig[key];
|
|
26417
26675
|
if (providerConfig && requiredFields.every((f) => Boolean(providerConfig[f]))) {
|
|
26418
|
-
const createFn = (await import("./auth-
|
|
26676
|
+
const createFn = (await import("./auth-Dkvo2tTJ.js").then((n) => n.t))[factory];
|
|
26419
26677
|
oauthProviders.push(createFn(providerConfig));
|
|
26420
26678
|
}
|
|
26421
26679
|
}
|
|
@@ -26527,6 +26785,21 @@ async function createRebaseBackend(options) {
|
|
|
26527
26785
|
}));
|
|
26528
26786
|
if (announce) logger.info(`MCP endpoint mounted at ${publicUrl}${mcpPath} (OAuth at ${oauthBasePath})`);
|
|
26529
26787
|
};
|
|
26788
|
+
const mountWarnings = [];
|
|
26789
|
+
const warnOnMount = (group, message) => {
|
|
26790
|
+
mountWarnings.push({
|
|
26791
|
+
group,
|
|
26792
|
+
message,
|
|
26793
|
+
said: false
|
|
26794
|
+
});
|
|
26795
|
+
};
|
|
26796
|
+
const sayMountWarnings = (groups) => {
|
|
26797
|
+
for (const warning of mountWarnings) {
|
|
26798
|
+
if (warning.said || !groups.has(warning.group)) continue;
|
|
26799
|
+
warning.said = true;
|
|
26800
|
+
logger.warn(warning.message);
|
|
26801
|
+
}
|
|
26802
|
+
};
|
|
26530
26803
|
const adminSurfacesGated = !!authAdapter && (isAuthAdapter(config.auth) || !!config.auth.jwtSecret);
|
|
26531
26804
|
const adminGate = (scopes) => [
|
|
26532
26805
|
provideServices,
|
|
@@ -26538,9 +26811,9 @@ async function createRebaseBackend(options) {
|
|
|
26538
26811
|
}),
|
|
26539
26812
|
typeof scopes === "string" ? requireScope(scopes) : requireScopeByMethod(scopes)
|
|
26540
26813
|
];
|
|
26541
|
-
const applyAdminGate = (router, surface, scopes) => {
|
|
26814
|
+
const applyAdminGate = (router, surface, scopes, mountedWith = "admin") => {
|
|
26542
26815
|
if (!adminSurfacesGated) {
|
|
26543
|
-
|
|
26816
|
+
warnOnMount(mountedWith, `${surface} routes are mounted but DISABLED: no authentication is configured (no auth adapter and no auth.jwtSecret), so there is no way to tell an admin from an anonymous caller. They answer 501 until auth is configured.`);
|
|
26544
26817
|
router.use("/*", async (c) => c.json({ error: {
|
|
26545
26818
|
code: "ADMIN_SURFACE_UNAVAILABLE",
|
|
26546
26819
|
message: `${surface} is admin-only, and this server has no authentication configured to identify an admin with. Set auth.jwtSecret (or pass an AuthAdapter) to enable it.`
|
|
@@ -26591,18 +26864,31 @@ async function createRebaseBackend(options) {
|
|
|
26591
26864
|
let schemaEditorRoutes;
|
|
26592
26865
|
let schemaSourceEditor;
|
|
26593
26866
|
if (!schemaEditorOff && config.collectionsDir) try {
|
|
26594
|
-
const editorModule = await import("./schema-editor-routes-
|
|
26867
|
+
const editorModule = await import("./schema-editor-routes-Bsut0bCC.js");
|
|
26595
26868
|
schemaSourceEditor = editorModule.createSchemaSourceEditor(config.collectionsDir);
|
|
26596
26869
|
schemaEditorRoutes = editorModule.createSchemaEditorRoutes(schemaSourceEditor);
|
|
26597
26870
|
} catch (err) {
|
|
26598
26871
|
if (err?.code === "ERR_MODULE_NOT_FOUND") {
|
|
26599
26872
|
schemaEditorOff = {
|
|
26600
26873
|
code: "SCHEMA_EDITOR_MISSING_DEPENDENCY",
|
|
26601
|
-
message: "The schema editor needs `ts-morph`, which is not installed on this server.
|
|
26874
|
+
message: "The schema editor needs `ts-morph`, which is not installed on this server. " + tsMorphInstallSentence()
|
|
26602
26875
|
};
|
|
26603
|
-
logger.
|
|
26876
|
+
logger.debug(`Schema Editor disabled: ${schemaEditorOff.message}`);
|
|
26604
26877
|
} else throw err;
|
|
26605
26878
|
}
|
|
26879
|
+
/**
|
|
26880
|
+
* The missing `ts-morph`, said the first time the editor is asked for —
|
|
26881
|
+
* the admin panel's collection editor asks both schema surfaces for their
|
|
26882
|
+
* status — rather than at boot. Once per backend.
|
|
26883
|
+
*/
|
|
26884
|
+
let toldEditorNeedsTsMorph = false;
|
|
26885
|
+
const sayEditorNeedsTsMorph = async (_c, next) => {
|
|
26886
|
+
if (!toldEditorNeedsTsMorph && schemaEditorOff?.code === "SCHEMA_EDITOR_MISSING_DEPENDENCY") {
|
|
26887
|
+
toldEditorNeedsTsMorph = true;
|
|
26888
|
+
logger.warn(`Schema Editor disabled: ${schemaEditorOff.message}`);
|
|
26889
|
+
}
|
|
26890
|
+
await next();
|
|
26891
|
+
};
|
|
26606
26892
|
let liveSchemaSurface;
|
|
26607
26893
|
let schemaEditorSurface;
|
|
26608
26894
|
if (surfaces.admin) {
|
|
@@ -26611,6 +26897,7 @@ async function createRebaseBackend(options) {
|
|
|
26611
26897
|
read: "schema:read",
|
|
26612
26898
|
write: "schema:write"
|
|
26613
26899
|
});
|
|
26900
|
+
schemaEditorRouter.use("/*", sayEditorNeedsTsMorph);
|
|
26614
26901
|
schemaEditorRouter.get("/status", (c) => c.json(schemaEditorOff ? {
|
|
26615
26902
|
enabled: false,
|
|
26616
26903
|
reason: schemaEditorOff.message,
|
|
@@ -26632,7 +26919,7 @@ async function createRebaseBackend(options) {
|
|
|
26632
26919
|
if (err?.code !== "ERR_MODULE_NOT_FOUND") throw err;
|
|
26633
26920
|
remoteEditingOff = {
|
|
26634
26921
|
code: "SCHEMA_EDITOR_MISSING_DEPENDENCY",
|
|
26635
|
-
message:
|
|
26922
|
+
message: `Live schema editing rewrites collection source with \`ts-morph\`, which is not installed on this server. ${tsMorphInstallSentence()}`
|
|
26636
26923
|
};
|
|
26637
26924
|
logger.warn(`Live schema editing disabled: ${remoteEditingOff.message}`);
|
|
26638
26925
|
}
|
|
@@ -26653,6 +26940,7 @@ async function createRebaseBackend(options) {
|
|
|
26653
26940
|
const liveSchemaRouter = new Hono();
|
|
26654
26941
|
let undeployedCommit;
|
|
26655
26942
|
applyAdminGate(liveSchemaRouter, "Live schema editing", "schema:read");
|
|
26943
|
+
liveSchemaRouter.use("/*", sayEditorNeedsTsMorph);
|
|
26656
26944
|
const commitPaths = remoteRepo ? commitPathsFor(path.join("/", remoteRepo.collectionsPath ?? "config/collections"), "/") : collectionsDir && repositoryRoot ? commitPathsFor(collectionsDir, repositoryRoot) : void 0;
|
|
26657
26945
|
if (commitPaths) logger.debug("Live schema editing commit paths relocated", { commitPaths });
|
|
26658
26946
|
liveSchemaRouter.route("/", createLiveSchemaRoutes({
|
|
@@ -26869,7 +27157,7 @@ async function createRebaseBackend(options) {
|
|
|
26869
27157
|
realtime: false,
|
|
26870
27158
|
token: internalServiceKey,
|
|
26871
27159
|
fetch: async (input, init) => {
|
|
26872
|
-
return await inProcessApp().request(input, init);
|
|
27160
|
+
return await announcements.run(() => inProcessApp().request(input, init));
|
|
26873
27161
|
}
|
|
26874
27162
|
});
|
|
26875
27163
|
const serviceIdentity = SERVICE_IDENTITY;
|
|
@@ -26930,9 +27218,9 @@ async function createRebaseBackend(options) {
|
|
|
26930
27218
|
injectCallbackClient(driverResults.values(), serverClient);
|
|
26931
27219
|
let functionsSurface;
|
|
26932
27220
|
if ((config.functionsDir || config.functions) && surfaces.functions) {
|
|
26933
|
-
const { functionList, readFunctionsDirectory } = await import("./function-loader-
|
|
26934
|
-
const { createFunctionRoutes } = await import("./function-routes-
|
|
26935
|
-
const { createFunctionsRequestTimeout, resolveFunctionsTimeoutMs } = await import("./request-timeout-
|
|
27221
|
+
const { functionList, readFunctionsDirectory } = await import("./function-loader-KKDqTcIr.js").then((n) => n.t);
|
|
27222
|
+
const { createFunctionRoutes } = await import("./function-routes-RxrCHNuM.js").then((n) => n.n);
|
|
27223
|
+
const { createFunctionsRequestTimeout, resolveFunctionsTimeoutMs } = await import("./request-timeout-59crS-9u.js").then((n) => n.t);
|
|
26936
27224
|
const { functions: functionRecord, problems } = config.functions ? {
|
|
26937
27225
|
functions: config.functions,
|
|
26938
27226
|
problems: []
|
|
@@ -26982,7 +27270,7 @@ async function createRebaseBackend(options) {
|
|
|
26982
27270
|
else logger.warn(`Function routes are served, but no functions loaded from ${config.functionsDir ?? "the `functions` option"}. ` + (problems.length > 0 ? `${problems.length} file(s) were skipped — see the messages above.` : "The directory holds no .ts/.js function files."));
|
|
26983
27271
|
}
|
|
26984
27272
|
if (config.functionsUpstream && !surfaces.functions) {
|
|
26985
|
-
const { createFunctionsProxy } = await import("./proxy-
|
|
27273
|
+
const { createFunctionsProxy } = await import("./proxy-CH8Jsovg.js").then((n) => n.t);
|
|
26986
27274
|
functionsSurface = createFunctionsProxy({ upstream: config.functionsUpstream });
|
|
26987
27275
|
logger.info("Forwarding custom functions to another process", { upstream: config.functionsUpstream });
|
|
26988
27276
|
}
|
|
@@ -26991,10 +27279,10 @@ async function createRebaseBackend(options) {
|
|
|
26991
27279
|
/** How many cron jobs `start()` schedules in this process. */
|
|
26992
27280
|
let cronJobsToSchedule = 0;
|
|
26993
27281
|
if (surfaces.cron || (config.cronsDir || config.crons) && ownership.cronScheduler) {
|
|
26994
|
-
const { cronJobsFrom, readCronsDirectory } = await import("./cron-loader-
|
|
26995
|
-
const { CronScheduler } = await import("./cron-scheduler-
|
|
26996
|
-
const { createCronRoutes } = await import("./cron-routes-
|
|
26997
|
-
const { createCronStore } = await import("./cron-store-
|
|
27282
|
+
const { cronJobsFrom, readCronsDirectory } = await import("./cron-loader-Dj6lA4HQ.js").then((n) => n.t);
|
|
27283
|
+
const { CronScheduler } = await import("./cron-scheduler-B04ACoUE.js").then((n) => n.n);
|
|
27284
|
+
const { createCronRoutes } = await import("./cron-routes-CwbLZguO.js").then((n) => n.n);
|
|
27285
|
+
const { createCronStore } = await import("./cron-store-DkyfuLOH.js").then((n) => n.n);
|
|
26998
27286
|
const { crons: cronRecord, problems: cronProblems } = config.crons ? {
|
|
26999
27287
|
crons: config.crons,
|
|
27000
27288
|
problems: []
|
|
@@ -27023,7 +27311,7 @@ async function createRebaseBackend(options) {
|
|
|
27023
27311
|
applyAdminGate(cronRouter, "Cron", {
|
|
27024
27312
|
read: "cron:read",
|
|
27025
27313
|
write: "cron:write"
|
|
27026
|
-
});
|
|
27314
|
+
}, "cron");
|
|
27027
27315
|
cronRouter.route("/", createCronRoutes(cronScheduler, cronProblems.length));
|
|
27028
27316
|
cronSurface = cronRouter;
|
|
27029
27317
|
if (loadedCronJobs.length > 0 && !ownership.cronScheduler) logger.info("Cron jobs registered but NOT scheduled in this process", {
|
|
@@ -27055,7 +27343,7 @@ async function createRebaseBackend(options) {
|
|
|
27055
27343
|
}
|
|
27056
27344
|
let metricsHistory;
|
|
27057
27345
|
try {
|
|
27058
|
-
const { createMetricsHistory } = await import("./history-recorder-
|
|
27346
|
+
const { createMetricsHistory } = await import("./history-recorder-B2rkuhuy.js");
|
|
27059
27347
|
metricsHistory = createMetricsHistory(defaultDriver);
|
|
27060
27348
|
if (metricsHistory && provision.database) await metricsHistory.ensure();
|
|
27061
27349
|
} catch (err) {
|
|
@@ -27064,7 +27352,7 @@ async function createRebaseBackend(options) {
|
|
|
27064
27352
|
}
|
|
27065
27353
|
let jobQueue;
|
|
27066
27354
|
if (jobsOptions?.enabled) {
|
|
27067
|
-
const { createJobStore, createJobQueue } = await import("./jobs-
|
|
27355
|
+
const { createJobStore, createJobQueue } = await import("./jobs-h1WRq3cp.js").then((n) => n.t);
|
|
27068
27356
|
const store = createJobStore(defaultDriver);
|
|
27069
27357
|
if (store) {
|
|
27070
27358
|
if (provision.database) await store.ensureTable();
|
|
@@ -27104,7 +27392,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27104
27392
|
let logsSurface;
|
|
27105
27393
|
let metaSurface;
|
|
27106
27394
|
if (surfaces.admin) {
|
|
27107
|
-
const { createBackupRoutes, backupDestinationFromEnv, readBackupSchedule } = await import("./backup-
|
|
27395
|
+
const { createBackupRoutes, backupDestinationFromEnv, readBackupSchedule } = await import("./backup-B9m3cb3X.js").then((n) => n.t);
|
|
27108
27396
|
const backupRouter = new Hono();
|
|
27109
27397
|
applyAdminGate(backupRouter, "Backup", "backups:read");
|
|
27110
27398
|
const scheduler = cronScheduler;
|
|
@@ -27125,7 +27413,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27125
27413
|
rlsAuditSurface = rlsAuditRouter;
|
|
27126
27414
|
}
|
|
27127
27415
|
if (surfaces.admin) {
|
|
27128
|
-
const { createSqlExportRoutes } = await import("./sql-export-routes-
|
|
27416
|
+
const { createSqlExportRoutes } = await import("./sql-export-routes-DmB_uZ4l.js");
|
|
27129
27417
|
const sqlExportRouter = new Hono();
|
|
27130
27418
|
applyAdminGate(sqlExportRouter, "SQL export", "database:write");
|
|
27131
27419
|
sqlExportRouter.route("/", createSqlExportRoutes({ getAdmin: () => defaultDriver.admin }));
|
|
@@ -27155,14 +27443,14 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27155
27443
|
devMailSurface = devMailRouter;
|
|
27156
27444
|
}
|
|
27157
27445
|
if (surfaces.admin) {
|
|
27158
|
-
const { createLogsRoutes } = await import("./logs-routes-
|
|
27446
|
+
const { createLogsRoutes } = await import("./logs-routes-CBt7mMIL.js").then((n) => n.n);
|
|
27159
27447
|
const logsRouter = new Hono();
|
|
27160
27448
|
applyAdminGate(logsRouter, "Logs", "logs:read");
|
|
27161
27449
|
logsRouter.route("/", createLogsRoutes({}, { closeSignal: longLivedResponses.signal }));
|
|
27162
27450
|
logsSurface = logsRouter;
|
|
27163
27451
|
}
|
|
27164
27452
|
if (surfaces.meta) {
|
|
27165
|
-
const { createContractRoutes } = await import("./contract-routes-
|
|
27453
|
+
const { createContractRoutes } = await import("./contract-routes-CsFXW7j4.js").then((n) => n.t);
|
|
27166
27454
|
const contractRouter = new Hono();
|
|
27167
27455
|
if (adminSurfacesGated) contractRouter.use("/contract", ...adminGate("schema:read"));
|
|
27168
27456
|
else {
|
|
@@ -27170,7 +27458,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27170
27458
|
code: "CONTRACT_UNAVAILABLE",
|
|
27171
27459
|
message: "The project contract is only served when authentication is configured, because it describes every table and relation in the project."
|
|
27172
27460
|
} }, 404));
|
|
27173
|
-
|
|
27461
|
+
warnOnMount("meta", "Contract endpoint disabled: no auth is configured (no adapter or no jwtSecret), and it would otherwise expose the full collection schema to anyone. `/api/meta/schema-version` is still served.");
|
|
27174
27462
|
}
|
|
27175
27463
|
contractRouter.route("/", createContractRoutes({
|
|
27176
27464
|
collectionRegistry,
|
|
@@ -27231,7 +27519,8 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27231
27519
|
if (surfaces[surface]) return;
|
|
27232
27520
|
throw new Error(`routes.${surface}(): this backend was created with \`surfaces: { ${surface}: false }\`, so it built no ${surface} routes. Create it with the ${surface} surface on to mount them.`);
|
|
27233
27521
|
};
|
|
27234
|
-
|
|
27522
|
+
/** The groups as they are built. `mountInto` and the public {@link routes} below compose from these. */
|
|
27523
|
+
const groups = {
|
|
27235
27524
|
auth: () => {
|
|
27236
27525
|
requireSurface("auth");
|
|
27237
27526
|
const authGroup = group();
|
|
@@ -27280,6 +27569,25 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27280
27569
|
return group(metaSurface);
|
|
27281
27570
|
}
|
|
27282
27571
|
};
|
|
27572
|
+
/**
|
|
27573
|
+
* The groups an application mounts one by one. Asking for one is serving:
|
|
27574
|
+
* what the build said is written, and the group's own warnings with it.
|
|
27575
|
+
*/
|
|
27576
|
+
const servedGroup = (name) => () => {
|
|
27577
|
+
const router = groups[name]();
|
|
27578
|
+
announcements.serve();
|
|
27579
|
+
sayMountWarnings(/* @__PURE__ */ new Set([name]));
|
|
27580
|
+
return router;
|
|
27581
|
+
};
|
|
27582
|
+
const routes = {
|
|
27583
|
+
auth: servedGroup("auth"),
|
|
27584
|
+
data: servedGroup("data"),
|
|
27585
|
+
storage: servedGroup("storage"),
|
|
27586
|
+
functions: servedGroup("functions"),
|
|
27587
|
+
admin: servedGroup("admin"),
|
|
27588
|
+
cron: servedGroup("cron"),
|
|
27589
|
+
meta: servedGroup("meta")
|
|
27590
|
+
};
|
|
27283
27591
|
/** The base path the application last mounted at — where the SDK's socket dials by default. */
|
|
27284
27592
|
let mountedBasePath;
|
|
27285
27593
|
/**
|
|
@@ -27293,6 +27601,14 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27293
27601
|
*/
|
|
27294
27602
|
const mountInto = (app, basePathOption, announce) => {
|
|
27295
27603
|
const basePath = basePathOption || "/api";
|
|
27604
|
+
if (announce) {
|
|
27605
|
+
announcements.serve();
|
|
27606
|
+
const mounted = /* @__PURE__ */ new Set();
|
|
27607
|
+
if (surfaces.admin) mounted.add("admin");
|
|
27608
|
+
if (surfaces.cron && cronSurface) mounted.add("cron");
|
|
27609
|
+
if (surfaces.meta && metaSurface) mounted.add("meta");
|
|
27610
|
+
sayMountWarnings(mounted);
|
|
27611
|
+
}
|
|
27296
27612
|
installRootErrorHandler(app);
|
|
27297
27613
|
installUnmatchedApiEnvelope(app, basePath);
|
|
27298
27614
|
const middlewares = configureMiddlewares(app, basePath, isProduction, announce ? config : {
|
|
@@ -27300,8 +27616,8 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27300
27616
|
corsHandled: true
|
|
27301
27617
|
});
|
|
27302
27618
|
app.use(`${basePath}/*`, refuseUnhonouredImpersonation([`${basePath}/data`, `${basePath}/functions`]));
|
|
27303
|
-
if (surfaces.admin) app.route(`${basePath}/admin`,
|
|
27304
|
-
if (surfaces.auth) app.route(`${basePath}/auth`,
|
|
27619
|
+
if (surfaces.admin) app.route(`${basePath}/admin`, groups.admin());
|
|
27620
|
+
if (surfaces.auth) app.route(`${basePath}/auth`, groups.auth());
|
|
27305
27621
|
if (jwt.isConfigured() && surfaces.auth) {
|
|
27306
27622
|
app.route("/.well-known", createJwksRoutes(jwt));
|
|
27307
27623
|
if (announce && jwt.hasAsymmetricSigningKey()) logger.info("Access tokens are signed asymmetrically; public keys at /.well-known/jwks.json");
|
|
@@ -27309,12 +27625,12 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27309
27625
|
mountMcp(app, basePath, announce);
|
|
27310
27626
|
if (schemaEditorSurface) app.route(`${basePath}/schema-editor`, deprecatedAlias(schemaEditorSurface, `${basePath}/admin/schema-editor`));
|
|
27311
27627
|
if (surfaces.storage) {
|
|
27312
|
-
app.route(`${basePath}/storage`,
|
|
27628
|
+
app.route(`${basePath}/storage`, groups.storage());
|
|
27313
27629
|
if (storageCarriesUploadLimit) middlewares.exemptFromBodyLimit(`${basePath}/storage${STORAGE_UPLOAD_PATH}`);
|
|
27314
27630
|
else if (announce) logger.info("Storage not configured — /storage/sources lists none, every other /storage route returns 501 STORAGE_NOT_CONFIGURED");
|
|
27315
27631
|
}
|
|
27316
27632
|
if (surfaces.data) {
|
|
27317
|
-
app.route(`${basePath}/data`,
|
|
27633
|
+
app.route(`${basePath}/data`, groups.data());
|
|
27318
27634
|
if (announce && activeCollections.length === 0) logger.info("No collections to serve — /data returns 404 NO_COLLECTIONS");
|
|
27319
27635
|
mountOpenApiDocs(app, basePath, {
|
|
27320
27636
|
enableSwagger: config.enableSwagger,
|
|
@@ -27327,15 +27643,15 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27327
27643
|
});
|
|
27328
27644
|
}
|
|
27329
27645
|
if (functionsSurface) {
|
|
27330
|
-
app.route(`${basePath}/functions`,
|
|
27646
|
+
app.route(`${basePath}/functions`, groups.functions());
|
|
27331
27647
|
if (announce) logger.debug("Function routes mounted", { path: `${basePath}/functions` });
|
|
27332
27648
|
}
|
|
27333
27649
|
if (surfaces.cron && cronSurface) {
|
|
27334
|
-
app.route(`${basePath}/admin/cron`,
|
|
27650
|
+
app.route(`${basePath}/admin/cron`, groups.cron());
|
|
27335
27651
|
app.route(`${basePath}/cron`, deprecatedAlias(cronSurface, `${basePath}/admin/cron`));
|
|
27336
27652
|
}
|
|
27337
27653
|
if (logsSurface) app.route(`${basePath}/logs`, deprecatedAlias(logsSurface, `${basePath}/admin/logs`));
|
|
27338
|
-
if (surfaces.meta && metaSurface) app.route(`${basePath}/meta`,
|
|
27654
|
+
if (surfaces.meta && metaSurface) app.route(`${basePath}/meta`, groups.meta());
|
|
27339
27655
|
if (announce) logger.debug("Rebase backend mounted", { basePath });
|
|
27340
27656
|
};
|
|
27341
27657
|
let inProcess;
|
|
@@ -27348,6 +27664,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27348
27664
|
};
|
|
27349
27665
|
let realtimeAttached = false;
|
|
27350
27666
|
const attachRealtime = async (server, attachOptions = {}) => {
|
|
27667
|
+
announcements.serve();
|
|
27351
27668
|
if (!surfaces.realtime || !defaultBootstrapper.initializeWebsockets || !effectiveRealtimeService) return;
|
|
27352
27669
|
if (realtimeAttached) throw new Error("attachRealtime(): this backend's websocket is already attached to a server.");
|
|
27353
27670
|
realtimeAttached = true;
|
|
@@ -27365,6 +27682,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27365
27682
|
let authTokenSweep;
|
|
27366
27683
|
let historyRetentionSweep;
|
|
27367
27684
|
const start = () => {
|
|
27685
|
+
announcements.serve();
|
|
27368
27686
|
if (started) return;
|
|
27369
27687
|
started = true;
|
|
27370
27688
|
runInBackendScope(ownScope, startTimers);
|
|
@@ -27406,7 +27724,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27406
27724
|
persistClaims: config.cronPersistence !== false
|
|
27407
27725
|
});
|
|
27408
27726
|
};
|
|
27409
|
-
const shutdown = (timeoutMs, shutdownOptions = {}) => createShutdown({
|
|
27727
|
+
const shutdown = (timeoutMs, shutdownOptions = {}) => announcements.run(() => createShutdown({
|
|
27410
27728
|
server: shutdownOptions.server,
|
|
27411
27729
|
cronScheduler,
|
|
27412
27730
|
jobQueue,
|
|
@@ -27417,7 +27735,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27417
27735
|
realtimeServices,
|
|
27418
27736
|
closeLongLivedResponses: () => longLivedResponses.abort(),
|
|
27419
27737
|
emailService
|
|
27420
|
-
})(timeoutMs);
|
|
27738
|
+
})(timeoutMs));
|
|
27421
27739
|
logger.debug("Rebase Backend Initialized");
|
|
27422
27740
|
return {
|
|
27423
27741
|
rebase: serverSingleton,
|
|
@@ -27436,7 +27754,7 @@ Use a driver that supports the job queue, or remove the declarations. Refusing t
|
|
|
27436
27754
|
cronScheduler,
|
|
27437
27755
|
jobQueue,
|
|
27438
27756
|
routes,
|
|
27439
|
-
fetch: async (request) => await inProcessApp().fetch(request),
|
|
27757
|
+
fetch: async (request) => await announcements.run(() => inProcessApp().fetch(request)),
|
|
27440
27758
|
mount: (app, mountOptions = {}) => {
|
|
27441
27759
|
mountInto(app, mountOptions.basePath, true);
|
|
27442
27760
|
mountedBasePath = mountOptions.basePath || "/api";
|
|
@@ -29121,14 +29439,14 @@ function listenWithPortRetry(server, startPort, options) {
|
|
|
29121
29439
|
const onListening = () => {
|
|
29122
29440
|
cleanup();
|
|
29123
29441
|
const bound = boundPort(server) ?? port;
|
|
29124
|
-
if (portFileDir) {
|
|
29125
|
-
|
|
29126
|
-
|
|
29127
|
-
fs.writeFileSync(portFile, `${bound} ${startPort}`, "utf-8");
|
|
29128
|
-
} catch {}
|
|
29129
|
-
writeStateFile(portFileDir, bound, options?.serviceKey);
|
|
29442
|
+
if (!portFileDir) {
|
|
29443
|
+
resolve(bound);
|
|
29444
|
+
return;
|
|
29130
29445
|
}
|
|
29131
|
-
|
|
29446
|
+
heldByAnotherServer(portFileDir, bound).catch(() => false).then((held) => {
|
|
29447
|
+
if (!held) writeDevFiles(portFileDir, bound, startPort, options?.serviceKey);
|
|
29448
|
+
resolve(bound);
|
|
29449
|
+
});
|
|
29132
29450
|
};
|
|
29133
29451
|
/**
|
|
29134
29452
|
* Say which port was refused and which one is next.
|
|
@@ -29174,18 +29492,116 @@ function listenWithPortRetry(server, startPort, options) {
|
|
|
29174
29492
|
});
|
|
29175
29493
|
}
|
|
29176
29494
|
/**
|
|
29495
|
+
* What this process wrote into each project's `.rebase-dev-port`, by
|
|
29496
|
+
* directory, so that stopping removes the file only while it still says that.
|
|
29497
|
+
*/
|
|
29498
|
+
var writtenPortFiles = /* @__PURE__ */ new Map();
|
|
29499
|
+
function stateFilePath(dir) {
|
|
29500
|
+
return path$1.join(dir, ".rebase", "state.json");
|
|
29501
|
+
}
|
|
29502
|
+
/** The pid and port `.rebase/state.json` names, or `null` when there is none to read. */
|
|
29503
|
+
function readStateOwner(dir) {
|
|
29504
|
+
let raw;
|
|
29505
|
+
try {
|
|
29506
|
+
raw = fs.readFileSync(stateFilePath(dir), "utf-8");
|
|
29507
|
+
} catch {
|
|
29508
|
+
return null;
|
|
29509
|
+
}
|
|
29510
|
+
try {
|
|
29511
|
+
const parsed = JSON.parse(raw);
|
|
29512
|
+
if (typeof parsed !== "object" || parsed === null) return {
|
|
29513
|
+
pid: null,
|
|
29514
|
+
port: null
|
|
29515
|
+
};
|
|
29516
|
+
return {
|
|
29517
|
+
pid: "pid" in parsed && typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0 ? parsed.pid : null,
|
|
29518
|
+
port: "port" in parsed && typeof parsed.port === "number" && Number.isInteger(parsed.port) ? parsed.port : null
|
|
29519
|
+
};
|
|
29520
|
+
} catch {
|
|
29521
|
+
return {
|
|
29522
|
+
pid: null,
|
|
29523
|
+
port: null
|
|
29524
|
+
};
|
|
29525
|
+
}
|
|
29526
|
+
}
|
|
29527
|
+
/** Is *some* process with this pid running? EPERM means it is, as someone else. */
|
|
29528
|
+
function processAlive(pid) {
|
|
29529
|
+
try {
|
|
29530
|
+
process.kill(pid, 0);
|
|
29531
|
+
return true;
|
|
29532
|
+
} catch (error) {
|
|
29533
|
+
return error.code === "EPERM";
|
|
29534
|
+
}
|
|
29535
|
+
}
|
|
29536
|
+
/**
|
|
29537
|
+
* Whether `rebase dev` started this server.
|
|
29538
|
+
*
|
|
29539
|
+
* It states `REBASE_DEV_PORT_EXPLICIT`, true or false, on every backend it
|
|
29540
|
+
* spawns (see `options.explicit` above), so the variable reading as either at
|
|
29541
|
+
* all is the mark. The dev files are named for that server and describe it
|
|
29542
|
+
* first.
|
|
29543
|
+
*/
|
|
29544
|
+
function startedByRebaseDev() {
|
|
29545
|
+
return parseEnvBoolean(process.env.REBASE_DEV_PORT_EXPLICIT) !== void 0;
|
|
29546
|
+
}
|
|
29547
|
+
/**
|
|
29548
|
+
* Whether another server in this project holds the dev files, so this one must
|
|
29549
|
+
* leave them alone.
|
|
29550
|
+
*
|
|
29551
|
+
* `rebase start` beside `rebase dev` is the documented local check, and both
|
|
29552
|
+
* bind through {@link listenWithPortRetry}. The files used to belong to
|
|
29553
|
+
* whoever bound last: `start` took them over while it ran, so `rebase
|
|
29554
|
+
* api-keys`, `generate-sdk` and MCP discovery all reached it rather than dev,
|
|
29555
|
+
* and when it stopped it deleted them under a dev that was still serving —
|
|
29556
|
+
* `api-keys list` then answered "This project's server is not running".
|
|
29557
|
+
*
|
|
29558
|
+
* So the files belong to the server `state.json` names while that server is
|
|
29559
|
+
* still there: its pid alive, and its port answering, because a pid alone can
|
|
29560
|
+
* be a stranger's after a crash and a reboot. A server on the very port it
|
|
29561
|
+
* names is not "another" one — that is a `tsx watch` restart, or a crash this
|
|
29562
|
+
* server has replaced. A server `rebase dev` started takes the files over in
|
|
29563
|
+
* every case: they are its own.
|
|
29564
|
+
*/
|
|
29565
|
+
async function heldByAnotherServer(dir, bound) {
|
|
29566
|
+
if (startedByRebaseDev()) return false;
|
|
29567
|
+
const owner = readStateOwner(dir);
|
|
29568
|
+
if (!owner || owner.pid === null || owner.pid === process.pid) return false;
|
|
29569
|
+
if (owner.port === null || owner.port === bound) return false;
|
|
29570
|
+
if (!processAlive(owner.pid)) return false;
|
|
29571
|
+
return loopbackInUse(owner.port);
|
|
29572
|
+
}
|
|
29573
|
+
/** Write `.rebase-dev-port` and `.rebase/state.json` for a server this process just bound. */
|
|
29574
|
+
function writeDevFiles(dir, bound, requested, serviceKey) {
|
|
29575
|
+
try {
|
|
29576
|
+
const contents = `${bound} ${requested}`;
|
|
29577
|
+
fs.writeFileSync(path$1.join(dir, DEV_PORT_FILENAME), contents, "utf-8");
|
|
29578
|
+
writtenPortFiles.set(path$1.resolve(dir), contents);
|
|
29579
|
+
} catch {}
|
|
29580
|
+
writeStateFile(dir, bound, serviceKey);
|
|
29581
|
+
}
|
|
29582
|
+
/**
|
|
29177
29583
|
* Clean up the dev port file and state file (call on graceful shutdown).
|
|
29178
29584
|
*
|
|
29585
|
+
* Only this process's own: `state.json` while it names this pid, and
|
|
29586
|
+
* `.rebase-dev-port` while it holds what this process wrote and no other
|
|
29587
|
+
* server has taken `state.json` over. A `rebase start` that stopped beside a
|
|
29588
|
+
* running `rebase dev` used to delete dev's files — see `heldByAnotherServer`.
|
|
29589
|
+
*
|
|
29179
29590
|
* @internal Not part of the stable public API. See {@link listenWithPortRetry}.
|
|
29180
29591
|
*/
|
|
29181
29592
|
function cleanupDevPortFile(dir) {
|
|
29182
|
-
|
|
29183
|
-
|
|
29184
|
-
|
|
29593
|
+
const owner = readStateOwner(dir);
|
|
29594
|
+
const ownsState = owner?.pid === process.pid;
|
|
29595
|
+
if (ownsState) try {
|
|
29596
|
+
fs.unlinkSync(stateFilePath(dir));
|
|
29185
29597
|
} catch {}
|
|
29598
|
+
const key = path$1.resolve(dir);
|
|
29599
|
+
const wrote = writtenPortFiles.get(key);
|
|
29600
|
+
if (wrote === void 0 || !(ownsState || owner === null)) return;
|
|
29186
29601
|
try {
|
|
29187
|
-
const
|
|
29188
|
-
if (fs.
|
|
29602
|
+
const portFile = path$1.join(dir, DEV_PORT_FILENAME);
|
|
29603
|
+
if (fs.readFileSync(portFile, "utf-8") === wrote) fs.unlinkSync(portFile);
|
|
29604
|
+
writtenPortFiles.delete(key);
|
|
29189
29605
|
} catch {}
|
|
29190
29606
|
}
|
|
29191
29607
|
/**
|