@harperfast/harper 5.0.0-alpha.10 → 5.0.0-beta.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/bin/BinObjects.js +17 -0
- package/bin/cliOperations.js +157 -0
- package/bin/copyDb.ts +280 -0
- package/bin/harper.js +156 -0
- package/bin/install.js +15 -0
- package/bin/lite.js +5 -0
- package/bin/restart.js +201 -0
- package/bin/run.js +409 -0
- package/bin/status.js +65 -0
- package/bin/stop.js +22 -0
- package/bin/upgrade.js +134 -0
- package/components/Application.ts +646 -0
- package/components/ApplicationScope.ts +49 -0
- package/components/Component.ts +53 -0
- package/components/ComponentV1.ts +342 -0
- package/components/DEFAULT_CONFIG.ts +18 -0
- package/components/EntryHandler.ts +227 -0
- package/components/Logger.ts +14 -0
- package/components/OptionsWatcher.ts +354 -0
- package/components/PluginModule.ts +6 -0
- package/components/Scope.ts +329 -0
- package/components/componentLoader.ts +529 -0
- package/components/deriveCommonPatternBase.ts +31 -0
- package/components/deriveGlobOptions.ts +44 -0
- package/components/deriveURLPath.ts +57 -0
- package/components/operations.js +658 -0
- package/components/operationsValidation.js +246 -0
- package/components/packageComponent.ts +39 -0
- package/components/requestRestart.ts +26 -0
- package/components/resolveBaseURLPath.ts +38 -0
- package/components/status/ComponentStatus.ts +110 -0
- package/components/status/ComponentStatusRegistry.ts +251 -0
- package/components/status/api.ts +153 -0
- package/components/status/crossThread.ts +405 -0
- package/components/status/errors.ts +152 -0
- package/components/status/index.ts +44 -0
- package/components/status/internal.ts +65 -0
- package/components/status/registry.ts +12 -0
- package/components/status/types.ts +96 -0
- package/config/RootConfigWatcher.ts +59 -0
- package/config/configHelpers.ts +11 -0
- package/config/configUtils.js +967 -0
- package/config/harperConfigEnvVars.ts +641 -0
- package/dataLayer/CreateAttributeObject.js +25 -0
- package/dataLayer/CreateTableObject.js +11 -0
- package/dataLayer/DataLayerObjects.js +43 -0
- package/dataLayer/DeleteBeforeObject.js +22 -0
- package/dataLayer/DeleteObject.js +25 -0
- package/dataLayer/DropAttributeObject.js +11 -0
- package/dataLayer/GetBackupObject.js +22 -0
- package/dataLayer/InsertObject.js +24 -0
- package/dataLayer/ReadAuditLogObject.js +24 -0
- package/dataLayer/SQLSearch.js +1335 -0
- package/dataLayer/SearchByConditionsObject.js +61 -0
- package/dataLayer/SearchByHashObject.js +21 -0
- package/dataLayer/SearchObject.js +45 -0
- package/dataLayer/SqlSearchObject.js +14 -0
- package/dataLayer/UpdateObject.js +23 -0
- package/dataLayer/UpsertObject.js +23 -0
- package/dataLayer/bulkLoad.js +813 -0
- package/dataLayer/dataObjects/BulkLoadObjects.js +27 -0
- package/dataLayer/dataObjects/UpsertObject.js +23 -0
- package/dataLayer/delete.js +164 -0
- package/dataLayer/export.js +381 -0
- package/dataLayer/getBackup.js +40 -0
- package/dataLayer/harperBridge/BridgeMethods.js +81 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +633 -0
- package/dataLayer/harperBridge/bridgeUtility/insertUpdateReturnObj.js +28 -0
- package/dataLayer/harperBridge/bridgeUtility/insertUpdateValidate.js +88 -0
- package/dataLayer/harperBridge/harperBridge.js +21 -0
- package/dataLayer/harperBridge/lmdbBridge/LMDBBridge.js +119 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +19 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateAttribute.js +112 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateRecords.js +67 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateSchema.js +31 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbCreateTable.js +94 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteAuditLogsBefore.js +98 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDeleteRecords.js +89 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropAttribute.js +109 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropSchema.js +107 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbDropTable.js +137 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbFlush.js +35 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetBackup.js +111 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByHash.js +28 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbGetDataByValue.js +29 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbReadAuditLog.js +207 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByConditions.js +156 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByHash.js +21 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbSearchByValue.js +30 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbTransaction.js +19 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpdateRecords.js +64 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/lmdbUpsertRecords.js +70 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBCreateAttributeObject.js +22 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBDeleteTransactionObject.js +23 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBInsertTransactionObject.js +22 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBTransactionObject.js +23 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBUpdateTransactionObject.js +24 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/LMDBUpsertTransactionObject.js +24 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/TableSizeObject.js +25 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializeHashSearch.js +21 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +157 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCheckForNewAttributes.js +94 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbCreateTransactionsAuditEnvironment.js +39 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.js +34 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbProcessRows.js +100 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbSearch.js +371 -0
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbWriteTransaction.js +109 -0
- package/dataLayer/hdbInfoController.js +254 -0
- package/dataLayer/insert.js +266 -0
- package/dataLayer/readAuditLog.js +59 -0
- package/dataLayer/schema.js +366 -0
- package/dataLayer/schemaDescribe.js +289 -0
- package/dataLayer/search.js +60 -0
- package/dataLayer/transaction.js +17 -0
- package/dataLayer/update.js +124 -0
- package/dist/components/Logger.d.ts +12 -0
- package/dist/components/Logger.js +3 -0
- package/dist/components/Logger.js.map +1 -0
- package/dist/components/Scope.d.ts +14 -4
- package/dist/components/Scope.js +18 -10
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/componentLoader.js +16 -9
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/operations.js +2 -2
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.d.ts +1 -1
- package/dist/config/configUtils.js +1 -1
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/CreateTableObject.d.ts +2 -2
- package/dist/dataLayer/CreateTableObject.js +2 -2
- package/dist/dataLayer/CreateTableObject.js.map +1 -1
- package/dist/dataLayer/delete.d.ts +1 -1
- package/dist/dataLayer/schema.js +6 -5
- package/dist/dataLayer/schema.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +1 -1
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/resources/DatabaseTransaction.d.ts +1 -1
- package/dist/resources/IterableEventQueue.d.ts +1 -1
- package/dist/resources/LMDBTransaction.d.ts +5 -1
- package/dist/resources/Resource.d.ts +1 -1
- package/dist/resources/RocksIndexStore.d.ts +3 -3
- package/dist/resources/RocksTransactionLogStore.d.ts +6 -3
- package/dist/resources/Table.d.ts +15 -6
- package/dist/resources/Table.js +4 -1
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/read.js +32 -22
- package/dist/resources/analytics/read.js.map +1 -1
- package/dist/resources/analytics/write.js +3 -6
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/auditStore.d.ts +3 -3
- package/dist/resources/blob.d.ts +25 -2
- package/dist/resources/databases.d.ts +12 -2
- package/dist/resources/databases.js +22 -19
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/search.js +11 -5
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/transaction.d.ts +2 -1
- package/dist/security/auth.js +1 -1
- package/dist/security/auth.js.map +1 -1
- package/dist/security/cryptoHash.d.ts +2 -2
- package/dist/security/jsLoader.js +243 -66
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/keys.js +4 -5
- package/dist/security/keys.js.map +1 -1
- package/dist/security/user.js +3 -3
- package/dist/security/user.js.map +1 -1
- package/dist/server/REST.js +16 -2
- package/dist/server/REST.js.map +1 -1
- package/dist/server/Server.d.ts +2 -1
- package/dist/server/Server.js.map +1 -1
- package/dist/server/fastifyRoutes/plugins/hdbCore.d.ts +6 -1
- package/dist/server/fastifyRoutes.js +2 -0
- package/dist/server/fastifyRoutes.js.map +1 -1
- package/dist/server/http.js +12 -6
- package/dist/server/http.js.map +1 -1
- package/dist/server/jobs/JobObject.d.ts +3 -3
- package/dist/server/loadRootComponents.js +1 -0
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/operationsServer.js +3 -1
- package/dist/server/operationsServer.js.map +1 -1
- package/dist/server/serverHelpers/JSONStream.d.ts +3 -3
- package/dist/server/serverHelpers/Request.d.ts +5 -5
- package/dist/server/serverHelpers/requestTimePlugin.d.ts +1 -1
- package/dist/server/threads/manageThreads.d.ts +2 -2
- package/dist/server/threads/manageThreads.js +50 -35
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.d.ts +1 -1
- package/dist/sqlTranslator/deleteTranslator.d.ts +1 -1
- package/dist/utility/AWS/AWSConnector.d.ts +3 -2
- package/dist/utility/common_utils.d.ts +3 -3
- package/dist/utility/environment/systemInformation.d.ts +1 -0
- package/dist/utility/functions/date/dateFunctions.d.ts +11 -11
- package/dist/utility/globalSchema.d.ts +1 -1
- package/dist/utility/hdbTerms.d.ts +3 -0
- package/dist/utility/hdbTerms.js +3 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/installation.d.ts +2 -4
- package/dist/utility/installation.js.map +1 -1
- package/dist/utility/lmdb/commonUtility.d.ts +1 -0
- package/dist/utility/lmdb/deleteUtility.d.ts +1 -0
- package/dist/utility/lmdb/environmentUtility.d.ts +1 -0
- package/dist/utility/lmdb/searchUtility.d.ts +2 -1
- package/dist/utility/lmdb/writeUtility.d.ts +1 -0
- package/dist/utility/logging/harper_logger.d.ts +6 -6
- package/dist/utility/processManagement/processManagement.d.ts +1 -1
- package/dist/utility/processManagement/servicesConfig.d.ts +12 -6
- package/dist/validation/common_validators.d.ts +4 -3
- package/dist/validation/configValidator.d.ts +3 -2
- package/index.d.ts +56 -0
- package/index.js +41 -0
- package/json/systemSchema.json +373 -0
- package/launchServiceScripts/launchHarperDB.js +3 -0
- package/launchServiceScripts/utility/checkNodeVersion.js +15 -0
- package/package.json +21 -3
- package/resources/DatabaseTransaction.ts +378 -0
- package/resources/ErrorResource.ts +57 -0
- package/resources/IterableEventQueue.ts +94 -0
- package/resources/LMDBTransaction.ts +349 -0
- package/resources/RecordEncoder.ts +702 -0
- package/resources/RequestTarget.ts +134 -0
- package/resources/Resource.ts +789 -0
- package/resources/ResourceInterface.ts +221 -0
- package/resources/ResourceInterfaceV2.ts +53 -0
- package/resources/ResourceV2.ts +67 -0
- package/resources/Resources.ts +162 -0
- package/resources/RocksIndexStore.ts +70 -0
- package/resources/RocksTransactionLogStore.ts +352 -0
- package/resources/Table.ts +4527 -0
- package/resources/analytics/hostnames.ts +72 -0
- package/resources/analytics/metadata.ts +10 -0
- package/resources/analytics/read.ts +252 -0
- package/resources/analytics/write.ts +803 -0
- package/resources/auditStore.ts +556 -0
- package/resources/blob.ts +1268 -0
- package/resources/crdt.ts +125 -0
- package/resources/dataLoader.ts +527 -0
- package/resources/databases.ts +1290 -0
- package/resources/graphql.ts +221 -0
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +638 -0
- package/resources/indexes/customIndexes.ts +7 -0
- package/resources/indexes/vector.ts +38 -0
- package/resources/jsResource.ts +86 -0
- package/resources/loadEnv.ts +22 -0
- package/resources/login.ts +18 -0
- package/resources/openApi.ts +409 -0
- package/resources/registrationDeprecated.ts +8 -0
- package/resources/replayLogs.ts +136 -0
- package/resources/roles.ts +98 -0
- package/resources/search.ts +1301 -0
- package/resources/tracked.ts +584 -0
- package/resources/transaction.ts +89 -0
- package/resources/transactionBroadcast.ts +258 -0
- package/security/auth.ts +376 -0
- package/security/certificateVerification/certificateVerificationSource.ts +84 -0
- package/security/certificateVerification/configValidation.ts +107 -0
- package/security/certificateVerification/crlVerification.ts +623 -0
- package/security/certificateVerification/index.ts +121 -0
- package/security/certificateVerification/ocspVerification.ts +148 -0
- package/security/certificateVerification/pkijs-ed25519-patch.ts +188 -0
- package/security/certificateVerification/types.ts +128 -0
- package/security/certificateVerification/verificationConfig.ts +138 -0
- package/security/certificateVerification/verificationUtils.ts +447 -0
- package/security/cryptoHash.js +42 -0
- package/security/data_objects/PermissionAttributeResponseObject.js +15 -0
- package/security/data_objects/PermissionResponseObject.js +115 -0
- package/security/data_objects/PermissionTableResponseObject.js +20 -0
- package/security/fastifyAuth.js +169 -0
- package/security/impersonation.ts +160 -0
- package/security/jsLoader.ts +716 -0
- package/security/keys.js +948 -0
- package/security/permissionsTranslator.js +300 -0
- package/security/role.js +218 -0
- package/security/tokenAuthentication.ts +228 -0
- package/security/user.ts +449 -0
- package/server/DurableSubscriptionsSession.ts +503 -0
- package/server/REST.ts +407 -0
- package/server/Server.ts +89 -0
- package/server/fastifyRoutes/helpers/getCORSOptions.js +36 -0
- package/server/fastifyRoutes/helpers/getHeaderTimeoutConfig.js +15 -0
- package/server/fastifyRoutes/helpers/getServerOptions.js +33 -0
- package/server/fastifyRoutes/plugins/hdbCore.js +39 -0
- package/server/fastifyRoutes.ts +205 -0
- package/server/graphqlQuerying.ts +700 -0
- package/server/http.ts +640 -0
- package/server/itc/serverHandlers.js +161 -0
- package/server/itc/utility/ITCEventObject.js +10 -0
- package/server/jobs/JobObject.js +24 -0
- package/server/jobs/jobProcess.js +69 -0
- package/server/jobs/jobRunner.js +162 -0
- package/server/jobs/jobs.js +304 -0
- package/server/loadRootComponents.js +44 -0
- package/server/mqtt.ts +485 -0
- package/server/nodeName.ts +75 -0
- package/server/operationsServer.ts +313 -0
- package/server/serverHelpers/Headers.ts +108 -0
- package/server/serverHelpers/JSONStream.ts +269 -0
- package/server/serverHelpers/OperationFunctionObject.ts +13 -0
- package/server/serverHelpers/Request.ts +158 -0
- package/server/serverHelpers/contentTypes.ts +637 -0
- package/server/serverHelpers/requestTimePlugin.js +57 -0
- package/server/serverHelpers/serverHandlers.js +148 -0
- package/server/serverHelpers/serverUtilities.ts +473 -0
- package/server/serverRegistry.ts +8 -0
- package/server/static.ts +187 -0
- package/server/status/definitions.ts +37 -0
- package/server/status/index.ts +125 -0
- package/server/storageReclamation.ts +93 -0
- package/server/threads/itc.js +89 -0
- package/server/threads/manageThreads.js +594 -0
- package/server/threads/socketRouter.ts +360 -0
- package/server/threads/threadServer.js +279 -0
- package/server/throttle.ts +73 -0
- package/sqlTranslator/SelectValidator.js +330 -0
- package/sqlTranslator/alasqlFunctionImporter.js +62 -0
- package/sqlTranslator/deleteTranslator.js +67 -0
- package/sqlTranslator/index.js +242 -0
- package/sqlTranslator/sql_statement_bucket.js +472 -0
- package/static/defaultConfig.yaml +3 -0
- package/studio/web/HDBDogOnly.svg +78 -0
- package/studio/web/assets/PPRadioGrotesk-Bold-DDaUYG8E.woff +0 -0
- package/studio/web/assets/fa-brands-400-CEJbCg16.woff +0 -0
- package/studio/web/assets/fa-brands-400-CSYNqBb_.ttf +0 -0
- package/studio/web/assets/fa-brands-400-DnkPfk3o.eot +0 -0
- package/studio/web/assets/fa-brands-400-UxlILjvJ.woff2 +0 -0
- package/studio/web/assets/fa-brands-400-cH1MgKbP.svg +3717 -0
- package/studio/web/assets/fa-regular-400-BhTwtT8w.eot +0 -0
- package/studio/web/assets/fa-regular-400-D1vz6WBx.ttf +0 -0
- package/studio/web/assets/fa-regular-400-DFnMcJPd.woff +0 -0
- package/studio/web/assets/fa-regular-400-DGzu1beS.woff2 +0 -0
- package/studio/web/assets/fa-regular-400-gwj8Pxq-.svg +801 -0
- package/studio/web/assets/fa-solid-900-B4ZZ7kfP.svg +5034 -0
- package/studio/web/assets/fa-solid-900-B6Axprfb.eot +0 -0
- package/studio/web/assets/fa-solid-900-BUswJgRo.woff2 +0 -0
- package/studio/web/assets/fa-solid-900-DOXgCApm.woff +0 -0
- package/studio/web/assets/fa-solid-900-mxuxnBEa.ttf +0 -0
- package/studio/web/assets/index-BTgXJX9d.js +235 -0
- package/studio/web/assets/index-BTgXJX9d.js.map +1 -0
- package/studio/web/assets/index-C-GXfcup.js +37 -0
- package/studio/web/assets/index-C-GXfcup.js.map +1 -0
- package/studio/web/assets/index-PFlNdimM.js +2 -0
- package/studio/web/assets/index-PFlNdimM.js.map +1 -0
- package/studio/web/assets/index-Y2g_iFpU.css +1 -0
- package/studio/web/assets/index-jiPwkrsB.css +1 -0
- package/studio/web/assets/index.lazy-C3TJZJ4o.js +266 -0
- package/studio/web/assets/index.lazy-C3TJZJ4o.js.map +1 -0
- package/studio/web/assets/profiler-DotzgiCJ.js +2 -0
- package/studio/web/assets/profiler-DotzgiCJ.js.map +1 -0
- package/studio/web/assets/react-redux-VxUEx_mU.js +6 -0
- package/studio/web/assets/react-redux-VxUEx_mU.js.map +1 -0
- package/studio/web/assets/startRecording-B_9J9Csd.js +3 -0
- package/studio/web/assets/startRecording-B_9J9Csd.js.map +1 -0
- package/studio/web/fabric-signup-background.webp +0 -0
- package/studio/web/fabric-signup-text.png +0 -0
- package/studio/web/favicon_purple.png +0 -0
- package/studio/web/github-icon.svg +15 -0
- package/studio/web/harper-fabric_black.png +0 -0
- package/studio/web/harper-fabric_white.png +0 -0
- package/studio/web/harper-studio_white.png +0 -0
- package/studio/web/index.html +16 -0
- package/studio/web/running.css +148 -0
- package/studio/web/running.html +147 -0
- package/studio/web/running.js +111 -0
- package/upgrade/UpgradeObjects.js +13 -0
- package/upgrade/directives/directivesController.js +90 -0
- package/upgrade/directivesManager.js +139 -0
- package/upgrade/upgradePrompt.js +124 -0
- package/upgrade/upgradeUtilities.js +28 -0
- package/utility/AWS/AWSConnector.js +29 -0
- package/utility/OperationFunctionCaller.js +63 -0
- package/utility/assignCmdEnvVariables.js +62 -0
- package/utility/common_utils.js +867 -0
- package/utility/environment/environmentManager.js +208 -0
- package/utility/environment/systemInformation.js +355 -0
- package/utility/errors/commonErrors.js +267 -0
- package/utility/errors/hdbError.js +146 -0
- package/utility/functions/date/dateFunctions.js +65 -0
- package/utility/functions/geo.js +355 -0
- package/utility/functions/sql/alaSQLExtension.js +104 -0
- package/utility/globalSchema.js +35 -0
- package/utility/hdbTerms.ts +819 -0
- package/utility/install/checkJWTTokensExist.js +62 -0
- package/utility/install/harperdb.conf +15 -0
- package/utility/install/harperdb.service +14 -0
- package/utility/install/installer.js +635 -0
- package/utility/installation.ts +30 -0
- package/utility/lmdb/DBIDefinition.js +20 -0
- package/utility/lmdb/DeleteRecordsResponseObject.js +25 -0
- package/utility/lmdb/InsertRecordsResponseObject.js +22 -0
- package/utility/lmdb/OpenDBIObject.js +31 -0
- package/utility/lmdb/OpenEnvironmentObject.js +41 -0
- package/utility/lmdb/UpdateRecordsResponseObject.js +25 -0
- package/utility/lmdb/UpsertRecordsResponseObject.js +22 -0
- package/utility/lmdb/cleanLMDBMap.js +65 -0
- package/utility/lmdb/commonUtility.js +119 -0
- package/utility/lmdb/deleteUtility.js +128 -0
- package/utility/lmdb/environmentUtility.js +477 -0
- package/utility/lmdb/searchCursorFunctions.js +187 -0
- package/utility/lmdb/searchUtility.js +918 -0
- package/utility/lmdb/terms.js +57 -0
- package/utility/lmdb/writeUtility.js +407 -0
- package/utility/logging/harper_logger.js +876 -0
- package/utility/logging/logRotator.js +157 -0
- package/utility/logging/logger.ts +24 -0
- package/utility/logging/readLog.js +355 -0
- package/utility/logging/transactionLog.js +57 -0
- package/utility/mount_hdb.js +59 -0
- package/utility/npmUtilities.js +102 -0
- package/utility/operationPermissions.ts +112 -0
- package/utility/operation_authorization.js +836 -0
- package/utility/packageUtils.js +55 -0
- package/utility/password.ts +99 -0
- package/utility/processManagement/processManagement.js +187 -0
- package/utility/processManagement/servicesConfig.js +56 -0
- package/utility/scripts/restartHdb.js +24 -0
- package/utility/scripts/user_data.sh +13 -0
- package/utility/signalling.js +36 -0
- package/utility/terms/certificates.js +81 -0
- package/utility/when.ts +20 -0
- package/v1.d.ts +39 -0
- package/v1.js +41 -0
- package/v2.d.ts +39 -0
- package/v2.js +41 -0
- package/validation/bulkDeleteValidator.js +24 -0
- package/validation/check_permissions.js +19 -0
- package/validation/common_validators.js +95 -0
- package/validation/configValidator.js +331 -0
- package/validation/deleteValidator.js +15 -0
- package/validation/fileLoadValidator.js +153 -0
- package/validation/insertValidator.js +40 -0
- package/validation/installValidator.js +37 -0
- package/validation/readLogValidator.js +64 -0
- package/validation/role_validation.js +320 -0
- package/validation/schemaMetadataValidator.js +42 -0
- package/validation/searchValidator.js +166 -0
- package/validation/statusValidator.ts +66 -0
- package/validation/transactionLogValidator.js +33 -0
- package/validation/user_validation.js +55 -0
- package/validation/validationWrapper.js +105 -0
- package/dist/resources/analytics/profile.d.ts +0 -2
- package/dist/resources/analytics/profile.js +0 -144
- package/dist/resources/analytics/profile.js.map +0 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export function add(record, property, action) {
|
|
2
|
+
const previousValue = record[property];
|
|
3
|
+
if (typeof previousValue === 'bigint') {
|
|
4
|
+
record[property] = previousValue + BigInt(action.value);
|
|
5
|
+
} else if (isNaN(record[property])) record[property] = action.value;
|
|
6
|
+
else {
|
|
7
|
+
record[property] = previousValue + action.value;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
add.reverse = function (record, property, action) {
|
|
11
|
+
const previousValue = record[property];
|
|
12
|
+
if (typeof previousValue === 'bigint') {
|
|
13
|
+
record[property] = previousValue - BigInt(action.value);
|
|
14
|
+
} else if (!isNaN(record[property])) {
|
|
15
|
+
record[property] = previousValue - action.value;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const operations = {
|
|
19
|
+
add,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Rebuild a record update that has a timestamp before the provided newer update
|
|
24
|
+
* @param update
|
|
25
|
+
* @param newerUpdate
|
|
26
|
+
*/
|
|
27
|
+
export function rebuildUpdateBefore(update: any, newerUpdate: any, fullUpdate?: boolean) {
|
|
28
|
+
let newUpdate = null;
|
|
29
|
+
for (const key in update) {
|
|
30
|
+
if (key in newerUpdate) {
|
|
31
|
+
const newerValue = newerUpdate[key];
|
|
32
|
+
if (newerValue?.__op__) {
|
|
33
|
+
const value = update[key];
|
|
34
|
+
if (value?.__op__) {
|
|
35
|
+
if (value.__op__ === newerValue.__op__) {
|
|
36
|
+
// we only have add right now
|
|
37
|
+
if (!newUpdate) newUpdate = {};
|
|
38
|
+
newUpdate[key] = value;
|
|
39
|
+
} else throw new Error('Can not merge updates with different operations');
|
|
40
|
+
} else {
|
|
41
|
+
if (!newUpdate) newUpdate = {};
|
|
42
|
+
// start with the older value
|
|
43
|
+
newUpdate[key] = value;
|
|
44
|
+
// and apply the newer update
|
|
45
|
+
add(newUpdate, key, newerValue);
|
|
46
|
+
}
|
|
47
|
+
} else if (fullUpdate) {
|
|
48
|
+
// if the newer update has a direct non-CRDT value, it overwrites the older update, but if we are using a full copy, we need to include it
|
|
49
|
+
if (!newUpdate) newUpdate = {};
|
|
50
|
+
newUpdate[key] = newerValue;
|
|
51
|
+
} // else we can skip for a patch
|
|
52
|
+
} else {
|
|
53
|
+
// if the newer update does not have a value for this key, we can include it
|
|
54
|
+
if (!newUpdate) newUpdate = {};
|
|
55
|
+
newUpdate[key] = update[key];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return newUpdate;
|
|
59
|
+
}
|
|
60
|
+
export function applyReverse(record, update, unknowns: Set<string>) {
|
|
61
|
+
for (const key in update) {
|
|
62
|
+
const value = update[key];
|
|
63
|
+
if (value?.__op__) {
|
|
64
|
+
const reverse = operations[value.__op__]?.reverse;
|
|
65
|
+
if (reverse) reverse(record, key, { value: value.value });
|
|
66
|
+
else throw new Error(`Unsupported operation ${value.__op__}`);
|
|
67
|
+
} else {
|
|
68
|
+
unknowns.add(key);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Reconstruct the record state at a given timestamp by going back through the audit history and reversing any changes
|
|
75
|
+
* @param currentEntry
|
|
76
|
+
* @param timestamp
|
|
77
|
+
* @param store
|
|
78
|
+
* @returns
|
|
79
|
+
*/
|
|
80
|
+
export function getRecordAtTime(currentEntry, timestamp, store, tableId: number, recordId: any) {
|
|
81
|
+
const auditStore = store.rootStore.auditStore;
|
|
82
|
+
let record = { ...currentEntry.value };
|
|
83
|
+
let auditTime = currentEntry.localTime;
|
|
84
|
+
// Iterate in reverse through the record history, trying to reverse all changes
|
|
85
|
+
const unknowns = new Set<string>();
|
|
86
|
+
while (auditTime > timestamp) {
|
|
87
|
+
const auditEntry = auditStore.get(auditTime, tableId, recordId);
|
|
88
|
+
if (!auditEntry) break;
|
|
89
|
+
switch (auditEntry.type) {
|
|
90
|
+
case 'put':
|
|
91
|
+
record = auditEntry.getValue(store);
|
|
92
|
+
break;
|
|
93
|
+
case 'patch':
|
|
94
|
+
applyReverse(record, auditEntry.getValue(store), unknowns);
|
|
95
|
+
break;
|
|
96
|
+
case 'delete':
|
|
97
|
+
record = null;
|
|
98
|
+
}
|
|
99
|
+
auditTime = auditEntry.previousVersion;
|
|
100
|
+
}
|
|
101
|
+
// some patches may leave properties in an unknown state, so we need to fill in the blanks
|
|
102
|
+
// first we determine if there any unknown properties
|
|
103
|
+
// then continue to iterate back through the audit history, filling in the blanks
|
|
104
|
+
while (unknowns.size > 0 && auditTime > 0) {
|
|
105
|
+
const auditEntry = auditStore.get(auditTime, tableId, recordId);
|
|
106
|
+
let priorRecord: any;
|
|
107
|
+
switch (auditEntry.type) {
|
|
108
|
+
case 'put':
|
|
109
|
+
priorRecord = auditEntry.getValue(store);
|
|
110
|
+
break;
|
|
111
|
+
case 'patch':
|
|
112
|
+
priorRecord = auditEntry.getValue(store);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
for (const key in priorRecord) {
|
|
116
|
+
if (unknowns.has(key)) {
|
|
117
|
+
record[key] = priorRecord[key];
|
|
118
|
+
unknowns.delete(key);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
auditTime = auditEntry.previousVersion;
|
|
122
|
+
}
|
|
123
|
+
// finally return the record in the state it was at the requested timestamp
|
|
124
|
+
return record;
|
|
125
|
+
}
|
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
import { basename, extname } from 'node:path';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import { parseDocument } from 'yaml';
|
|
4
|
+
import { Databases, databases, table, Tables, tables } from './databases.ts';
|
|
5
|
+
import { getWorkerIndex } from '../server/threads/manageThreads';
|
|
6
|
+
import { HTTP_STATUS_CODES } from '../utility/errors/commonErrors.js';
|
|
7
|
+
import { ClientError } from '../utility/errors/hdbError.js';
|
|
8
|
+
import harperLogger from '../utility/logging/harper_logger.js';
|
|
9
|
+
import { Attribute } from './Table.ts';
|
|
10
|
+
import { FileEntry } from '../components/EntryHandler.ts';
|
|
11
|
+
|
|
12
|
+
const dataLoaderLogger = harperLogger.forComponent('dataLoader');
|
|
13
|
+
|
|
14
|
+
/** System table name for storing data loader hashes */
|
|
15
|
+
const DATA_LOADER_HASH_TABLE = 'hdb_dataloader_hash';
|
|
16
|
+
|
|
17
|
+
/** Lazy-initialized cache for the hash tracking table */
|
|
18
|
+
let _hashTrackingTable: ReturnType<typeof table>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Computes a deterministic hash of a record's content for tracking data file changes.
|
|
22
|
+
* The hash is computed from a stable JSON representation (sorted keys) to ensure
|
|
23
|
+
* the same content always produces the same hash, regardless of key order.
|
|
24
|
+
*
|
|
25
|
+
* @param record - The record object to hash
|
|
26
|
+
* @returns A hex string hash of the record content
|
|
27
|
+
*/
|
|
28
|
+
export function computeRecordHash(record: Record<string, any>): string {
|
|
29
|
+
// Sort keys for deterministic hashing
|
|
30
|
+
const sortedKeys = Object.keys(record).sort();
|
|
31
|
+
const sortedRecord: Record<string, any> = {};
|
|
32
|
+
for (const key of sortedKeys) {
|
|
33
|
+
sortedRecord[key] = record[key];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Compute hash of the stable JSON representation
|
|
37
|
+
const content = JSON.stringify(sortedRecord);
|
|
38
|
+
return createHash('sha256').update(content).digest('hex');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Gets or creates the hash tracking table in the system database.
|
|
43
|
+
* Lazy-initializes the table on first access.
|
|
44
|
+
*/
|
|
45
|
+
function getHashTrackingTable(databasesRef: Databases) {
|
|
46
|
+
// Always check databasesRef first (important for testing with mocks)
|
|
47
|
+
if (databasesRef.system && databasesRef.system[DATA_LOADER_HASH_TABLE]) {
|
|
48
|
+
return databasesRef.system[DATA_LOADER_HASH_TABLE];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Use cached table if available
|
|
52
|
+
if (_hashTrackingTable) {
|
|
53
|
+
return _hashTrackingTable;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Create the system table for tracking hashes
|
|
57
|
+
_hashTrackingTable = table({
|
|
58
|
+
database: 'system',
|
|
59
|
+
table: DATA_LOADER_HASH_TABLE,
|
|
60
|
+
attributes: [
|
|
61
|
+
{ name: 'id', type: 'string', isPrimaryKey: true }, // Format: "database:table:recordId"
|
|
62
|
+
{ name: 'hash', type: 'string' },
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return _hashTrackingTable;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Gets the stored hash for a record from the tracking table
|
|
71
|
+
*/
|
|
72
|
+
async function getStoredHash(
|
|
73
|
+
database: string | undefined,
|
|
74
|
+
tableName: string,
|
|
75
|
+
recordId: string,
|
|
76
|
+
databasesRef: Databases
|
|
77
|
+
): Promise<string | null> {
|
|
78
|
+
try {
|
|
79
|
+
const trackingTable = getHashTrackingTable(databasesRef);
|
|
80
|
+
const hashId = database ? `${database}:${tableName}:${recordId}` : `${tableName}:${recordId}`;
|
|
81
|
+
const hashRecord = await trackingTable.get(hashId);
|
|
82
|
+
return hashRecord?.hash || null;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
dataLoaderLogger.error?.(`Failed to get stored hash: ${error.message}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Stores the hash for a record in the tracking table
|
|
91
|
+
*/
|
|
92
|
+
async function storeHash(
|
|
93
|
+
database: string | undefined,
|
|
94
|
+
tableName: string,
|
|
95
|
+
recordId: string,
|
|
96
|
+
hash: string,
|
|
97
|
+
databasesRef: Databases
|
|
98
|
+
): Promise<void> {
|
|
99
|
+
try {
|
|
100
|
+
const trackingTable = getHashTrackingTable(databasesRef);
|
|
101
|
+
const hashId = database ? `${database}:${tableName}:${recordId}` : `${tableName}:${recordId}`;
|
|
102
|
+
await trackingTable.put({ id: hashId, hash });
|
|
103
|
+
} catch (error) {
|
|
104
|
+
dataLoaderLogger.error?.(`Failed to store hash: ${error.message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Set up file handlers for data files and loads them into the appropriate tables
|
|
110
|
+
*/
|
|
111
|
+
export function handleApplication(scope) {
|
|
112
|
+
// Early return if this isn't worker zero
|
|
113
|
+
// Currently using getWorkerIndex() over server.workerIndex to appease ts. The latter defined in manageThreads.js.
|
|
114
|
+
if (getWorkerIndex() !== 0) {
|
|
115
|
+
// debug and return
|
|
116
|
+
dataLoaderLogger.debug?.('Skipping data loader initialization on non-primary worker');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handle all files that match the pattern in the config
|
|
121
|
+
// Note: Using .then() instead of async/await to avoid the performance overhead
|
|
122
|
+
// of additional promise wrappers created by the async/await syntax sugar
|
|
123
|
+
scope.handleEntry(function handleDataLoaderEntry(entry) {
|
|
124
|
+
// Return early if not adding or updating a file
|
|
125
|
+
if (entry.entryType !== 'file' || entry.eventType === 'unlink') {
|
|
126
|
+
return Promise.resolve();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return loadDataFile(entry, tables, databases).then((result) => {
|
|
130
|
+
dataLoaderLogger.debug?.('Data loader processed file: %s: %s', basename(entry.absolutePath), result.message);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* This component handles data loading from YAML or JSON files into user-defined tables.
|
|
137
|
+
* @param { contents, absolutePath, stats } - File entry content buffer, absolute path, and stats
|
|
138
|
+
* @param tablesRef - Reference to tables object (local const for testing)
|
|
139
|
+
* @param databasesRef - Reference to databases object (local const for testing)
|
|
140
|
+
*/
|
|
141
|
+
|
|
142
|
+
export async function loadDataFile({ contents, absolutePath }: FileEntry, tablesRef: Tables, databasesRef: Databases) {
|
|
143
|
+
const fileExt = extname(absolutePath) || 'unknown';
|
|
144
|
+
let data: DataFileFormat;
|
|
145
|
+
|
|
146
|
+
// Need to grab the file extension to determine how to parse the content
|
|
147
|
+
try {
|
|
148
|
+
if (fileExt === '.yaml' || fileExt === '.yml') {
|
|
149
|
+
data = parseDocument(contents.toString()).toJSON();
|
|
150
|
+
} else if (fileExt === '.json') {
|
|
151
|
+
data = JSON.parse(contents.toString());
|
|
152
|
+
} else {
|
|
153
|
+
throw new UnsupportedFileExtensionError(absolutePath, fileExt);
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
// Re-throw DataLoaderErrors
|
|
157
|
+
if (error instanceof DataLoaderError) {
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Otherwise wrap in a FileParseError and throw
|
|
162
|
+
throw new FileParseError(absolutePath, error);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Ensure data exists. I.E. the file is not empty
|
|
166
|
+
if (!data) {
|
|
167
|
+
throw new EmptyFileError(absolutePath);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const { database, table: tableName, records } = data;
|
|
171
|
+
|
|
172
|
+
// Validate the data format
|
|
173
|
+
if (!tableName) {
|
|
174
|
+
throw new MissingRequiredPropertyError(absolutePath, 'table');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!records) {
|
|
178
|
+
throw new MissingRequiredPropertyError(absolutePath, 'records');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!Array.isArray(records)) {
|
|
182
|
+
throw new InvalidPropertyTypeError(absolutePath, 'records', 'array');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// tableIdentifier is used for logging and error messages
|
|
186
|
+
const tableIdentifier = database ? `${database}.${tableName}` : tableName;
|
|
187
|
+
|
|
188
|
+
// Don't allow loading data into the system database
|
|
189
|
+
if (database?.toLowerCase() === 'system') {
|
|
190
|
+
throw new SystemDatabaseError(database, tableName);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
// Try to get the table from global tables if it exists
|
|
195
|
+
let tableRef;
|
|
196
|
+
|
|
197
|
+
// If a database is specified, check if the table exists in that database
|
|
198
|
+
if (database && databasesRef[database] && databasesRef[database][tableName]) {
|
|
199
|
+
dataLoaderLogger.debug?.(`Using existing table ${tableIdentifier} from database tables`);
|
|
200
|
+
tableRef = databasesRef[database][tableName];
|
|
201
|
+
}
|
|
202
|
+
// If no database is specified, check if the table exists in the global tables
|
|
203
|
+
else if (tablesRef && tablesRef[tableName]) {
|
|
204
|
+
dataLoaderLogger.debug?.(`Using existing table ${tableIdentifier} from global tables`);
|
|
205
|
+
tableRef = tablesRef[tableName];
|
|
206
|
+
} else {
|
|
207
|
+
// Table doesn't exist. Try to infer the schema from the first record
|
|
208
|
+
dataLoaderLogger.debug?.(`Table ${tableIdentifier} not found, creating new table`);
|
|
209
|
+
|
|
210
|
+
// Extract attributes from the first record for the ensureTable call
|
|
211
|
+
const attributes: Attribute[] = [];
|
|
212
|
+
if (records.length > 0) {
|
|
213
|
+
const firstRecord = records[0];
|
|
214
|
+
Object.keys(firstRecord)
|
|
215
|
+
.map((attrName) => {
|
|
216
|
+
const attr: Attribute = { name: attrName, type: typeof firstRecord[attrName] };
|
|
217
|
+
// If the attribute is 'id', mark it as primary key
|
|
218
|
+
if (attrName === 'id') {
|
|
219
|
+
attr.isPrimaryKey = true;
|
|
220
|
+
}
|
|
221
|
+
return attr;
|
|
222
|
+
})
|
|
223
|
+
.forEach((attr) => {
|
|
224
|
+
attributes.push(attr);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
tableRef = await table({
|
|
229
|
+
database,
|
|
230
|
+
table: tableName,
|
|
231
|
+
attributes,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Process records with timestamp comparison
|
|
236
|
+
// Count metrics
|
|
237
|
+
const dataFIleRecords = records.length;
|
|
238
|
+
let newRecords = 0;
|
|
239
|
+
let updatedRecords = 0;
|
|
240
|
+
let skippedRecords = 0;
|
|
241
|
+
|
|
242
|
+
// Process each record in a batch to avoid excessive memory usage
|
|
243
|
+
const batchSize = 100; // Process in batches of 100 records
|
|
244
|
+
|
|
245
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
246
|
+
const batch = records.slice(i, i + batchSize);
|
|
247
|
+
const batchPromises: Array<() => Promise<any>> = [];
|
|
248
|
+
|
|
249
|
+
for (const newRecord of batch) {
|
|
250
|
+
// Wrap in an async function to handle errors individually
|
|
251
|
+
batchPromises.push(async () => {
|
|
252
|
+
try {
|
|
253
|
+
// Get existing record with the same ID if it exists
|
|
254
|
+
let existingRecord: Record<string, any> | null = null;
|
|
255
|
+
const recordId = newRecord.id;
|
|
256
|
+
|
|
257
|
+
if (recordId !== undefined) {
|
|
258
|
+
existingRecord = await tableRef.get(recordId);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Compute hash of the new record from the data file
|
|
262
|
+
const newRecordHash = computeRecordHash(newRecord);
|
|
263
|
+
|
|
264
|
+
if (!existingRecord) {
|
|
265
|
+
// If the record doesn't exist yet, insert it
|
|
266
|
+
newRecords++;
|
|
267
|
+
const result = await tableRef.put(newRecord);
|
|
268
|
+
// Store the hash in the tracking table
|
|
269
|
+
await storeHash(database, tableName, recordId, newRecordHash, databasesRef);
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Check if there's a stored hash for this record
|
|
274
|
+
const existingHash = await getStoredHash(database, tableName, recordId, databasesRef);
|
|
275
|
+
|
|
276
|
+
if (!existingHash) {
|
|
277
|
+
// No hash means this record wasn't loaded by the data loader
|
|
278
|
+
// (likely created via operations API or other means)
|
|
279
|
+
// Don't overwrite user-created records
|
|
280
|
+
skippedRecords++;
|
|
281
|
+
return Promise.resolve({ inserted: 0, updated: 0 });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Compute hash of only the fields that exist in the data file
|
|
285
|
+
// This allows users to add extra fields without triggering a "modified" detection
|
|
286
|
+
// Note: This is a simple top-level field comparison - nested object changes
|
|
287
|
+
// within data file fields will still be detected as modifications
|
|
288
|
+
const existingRecordSubset: Record<string, any> = {};
|
|
289
|
+
for (const key of Object.keys(newRecord)) {
|
|
290
|
+
if (key in existingRecord) {
|
|
291
|
+
existingRecordSubset[key] = existingRecord[key];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const existingRecordHash = computeRecordHash(existingRecordSubset);
|
|
295
|
+
|
|
296
|
+
if (existingRecordHash !== existingHash) {
|
|
297
|
+
// The existing record's data file fields don't match the stored hash,
|
|
298
|
+
// meaning those fields were modified externally (via operations API, etc.)
|
|
299
|
+
// Don't overwrite user-modified records
|
|
300
|
+
skippedRecords++;
|
|
301
|
+
return Promise.resolve({ inserted: 0, updated: 0 });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Compare hashes to detect actual content changes in the data file
|
|
305
|
+
if (newRecordHash !== existingHash) {
|
|
306
|
+
// Hash differs - the data file content has changed
|
|
307
|
+
// Use patch to update only the fields from the data file,
|
|
308
|
+
// preserving any additional fields the user may have added
|
|
309
|
+
updatedRecords++;
|
|
310
|
+
await tableRef.patch(recordId, newRecord);
|
|
311
|
+
await storeHash(database, tableName, recordId, newRecordHash, databasesRef);
|
|
312
|
+
// Return a result indicating update (patch doesn't return a value)
|
|
313
|
+
return { updated: 1 };
|
|
314
|
+
} else {
|
|
315
|
+
// Hash matches - content hasn't changed, skip update
|
|
316
|
+
skippedRecords++;
|
|
317
|
+
return Promise.resolve({ inserted: 0, updated: 0 });
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
// For individual record errors, we log but continue processing other records
|
|
321
|
+
// This allows partial success in data loading
|
|
322
|
+
if (error instanceof DataLoaderError) {
|
|
323
|
+
dataLoaderLogger.error?.(`Record processing error: ${error.message}`);
|
|
324
|
+
} else {
|
|
325
|
+
const recError = new RecordProcessingError(tableIdentifier, error);
|
|
326
|
+
dataLoaderLogger.error?.(`Record processing error: ${recError.message}`);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Don't throw, just return a failed operation result
|
|
330
|
+
return Promise.resolve({ inserted: 0, updated: 0, error: error.message });
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Execute batch promises. Currently not doing anything about errors or the put() results.
|
|
336
|
+
await Promise.all(batchPromises.map((fn) => fn()));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Return a single result object
|
|
340
|
+
if (newRecords > 0 || updatedRecords > 0) {
|
|
341
|
+
let message = `Loaded ${newRecords} new and updated ${updatedRecords} records in ${tableIdentifier}`;
|
|
342
|
+
if (skippedRecords > 0) {
|
|
343
|
+
message += ` (${skippedRecords} records skipped)`;
|
|
344
|
+
}
|
|
345
|
+
dataLoaderLogger.info?.(message);
|
|
346
|
+
|
|
347
|
+
return new DataLoaderResult(absolutePath, database, tableName, 'success', newRecords + updatedRecords, message);
|
|
348
|
+
} else if (skippedRecords > 0) {
|
|
349
|
+
const message = `All ${skippedRecords} records in ${tableIdentifier} already up-to-date`;
|
|
350
|
+
dataLoaderLogger.info?.(message);
|
|
351
|
+
|
|
352
|
+
return new DataLoaderResult(absolutePath, database, tableName, 'skipped', dataFIleRecords, message);
|
|
353
|
+
} else {
|
|
354
|
+
const message = `No records to process in ${tableIdentifier}`;
|
|
355
|
+
dataLoaderLogger.info?.(message);
|
|
356
|
+
|
|
357
|
+
return new DataLoaderResult(absolutePath, database, tableName, 'success', 0, message);
|
|
358
|
+
}
|
|
359
|
+
} catch (error) {
|
|
360
|
+
// If it's already one of our custom errors, just rethrow
|
|
361
|
+
if (error instanceof DataLoaderError) {
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Wrap and throw other errors
|
|
366
|
+
throw new RecordProcessingError(tableIdentifier, error);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Custom errors for the dataLoader. These are thrown during startup validation to fail early
|
|
372
|
+
* rather than continuing with invalid data.
|
|
373
|
+
*/
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Base class for DataLoader specific errors
|
|
377
|
+
*/
|
|
378
|
+
export class DataLoaderError extends ClientError {
|
|
379
|
+
constructor(message: string, statusCode: number = HTTP_STATUS_CODES.BAD_REQUEST) {
|
|
380
|
+
super(message, statusCode);
|
|
381
|
+
this.name = 'DataLoaderError';
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Error thrown when a file has an unsupported extension
|
|
387
|
+
*/
|
|
388
|
+
export class UnsupportedFileExtensionError extends DataLoaderError {
|
|
389
|
+
constructor(filePath: string, extension: string) {
|
|
390
|
+
super(
|
|
391
|
+
`Unsupported file extension in ${basename(filePath)}: ${extension}. Only YAML and JSON files are supported.`,
|
|
392
|
+
HTTP_STATUS_CODES.BAD_REQUEST
|
|
393
|
+
);
|
|
394
|
+
this.name = 'UnsupportedFileExtensionError';
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Error thrown when a file cannot be parsed
|
|
400
|
+
*/
|
|
401
|
+
export class FileParseError extends DataLoaderError {
|
|
402
|
+
constructor(filePath: string, originalError: Error) {
|
|
403
|
+
super(`Failed to parse data file ${basename(filePath)}: ${originalError.message}`, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
404
|
+
this.name = 'FileParseError';
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Error thrown when a file is empty or invalid
|
|
410
|
+
*/
|
|
411
|
+
export class EmptyFileError extends DataLoaderError {
|
|
412
|
+
constructor(filePath: string) {
|
|
413
|
+
super(`Data file ${basename(filePath)} is empty or invalid`, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
414
|
+
this.name = 'EmptyFileError';
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Error thrown when a data file is missing required properties
|
|
420
|
+
*/
|
|
421
|
+
export class MissingRequiredPropertyError extends DataLoaderError {
|
|
422
|
+
constructor(filePath: string, property: string) {
|
|
423
|
+
super(`Data file ${basename(filePath)} is missing required "${property}" property`, HTTP_STATUS_CODES.BAD_REQUEST);
|
|
424
|
+
this.name = 'MissingRequiredPropertyError';
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Error thrown when a property has an invalid type
|
|
430
|
+
*/
|
|
431
|
+
export class InvalidPropertyTypeError extends DataLoaderError {
|
|
432
|
+
constructor(filePath: string, property: string, expectedType: string) {
|
|
433
|
+
super(
|
|
434
|
+
`Data file ${basename(filePath)} has invalid "${property}" property, expected ${expectedType}`,
|
|
435
|
+
HTTP_STATUS_CODES.BAD_REQUEST
|
|
436
|
+
);
|
|
437
|
+
this.name = 'InvalidPropertyTypeError';
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Error thrown when trying to load data into the system database
|
|
443
|
+
*/
|
|
444
|
+
export class SystemDatabaseError extends DataLoaderError {
|
|
445
|
+
constructor(database: string, table: string) {
|
|
446
|
+
super(`Cannot load data into system database: ${database}.${table}`, HTTP_STATUS_CODES.FORBIDDEN);
|
|
447
|
+
this.name = 'SystemDatabaseError';
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Error thrown when record processing fails
|
|
453
|
+
*/
|
|
454
|
+
export class RecordProcessingError extends DataLoaderError {
|
|
455
|
+
constructor(tableIdentifier: string, originalError: Error) {
|
|
456
|
+
super(
|
|
457
|
+
`Failed to process record in ${tableIdentifier}: ${originalError.message}`,
|
|
458
|
+
HTTP_STATUS_CODES.INTERNAL_SERVER_ERROR
|
|
459
|
+
);
|
|
460
|
+
this.name = 'RecordProcessingError';
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Define the structure of the data file format
|
|
465
|
+
export interface DataFileFormat {
|
|
466
|
+
database?: string; // Optional database name
|
|
467
|
+
table: string; // Required table name
|
|
468
|
+
records: Record<string, any>[]; // Array of records to load
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Define the class for data loader results
|
|
472
|
+
export class DataLoaderResult {
|
|
473
|
+
#filePath: string; // Path to the data file
|
|
474
|
+
#database: string; // Database name
|
|
475
|
+
#table: string; // Table name
|
|
476
|
+
#status: string; // Status of the operation
|
|
477
|
+
#count: number; // Number of records processed
|
|
478
|
+
#message: string; // Message about the operation
|
|
479
|
+
|
|
480
|
+
constructor(
|
|
481
|
+
filePath: string,
|
|
482
|
+
database: string | null | undefined,
|
|
483
|
+
table: string | null,
|
|
484
|
+
status: string,
|
|
485
|
+
count: number,
|
|
486
|
+
message: string
|
|
487
|
+
) {
|
|
488
|
+
this.#filePath = filePath;
|
|
489
|
+
this.#database = database || 'unknown';
|
|
490
|
+
this.#table = table || 'unknown';
|
|
491
|
+
this.#status = status;
|
|
492
|
+
this.#count = count;
|
|
493
|
+
this.#message = message;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Getters
|
|
497
|
+
get filePath(): string {
|
|
498
|
+
return this.#filePath;
|
|
499
|
+
}
|
|
500
|
+
get database(): string {
|
|
501
|
+
return this.#database;
|
|
502
|
+
}
|
|
503
|
+
get table(): string {
|
|
504
|
+
return this.#table;
|
|
505
|
+
}
|
|
506
|
+
get status(): string {
|
|
507
|
+
return this.#status;
|
|
508
|
+
}
|
|
509
|
+
get count(): number {
|
|
510
|
+
return this.#count;
|
|
511
|
+
}
|
|
512
|
+
get message(): string {
|
|
513
|
+
return this.#message;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Methods to convert to JSON (for serialization)
|
|
517
|
+
toJSON() {
|
|
518
|
+
return {
|
|
519
|
+
filePath: this.#filePath,
|
|
520
|
+
database: this.#database,
|
|
521
|
+
table: this.#table,
|
|
522
|
+
status: this.#status,
|
|
523
|
+
count: this.#count,
|
|
524
|
+
message: this.#message,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
}
|