@elyx-code/editor-ui 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +109 -0
- package/src/App.tsx +31 -0
- package/src/Router.tsx +115 -0
- package/src/__mocks__/defaultModuleMock.ts +1 -0
- package/src/__mocks__/fileMock.ts +1 -0
- package/src/__mocks__/styleMock.ts +1 -0
- package/src/assets/Clock-11.1s-18px.svg +16 -0
- package/src/assets/Clock-11.1s-28px.svg +16 -0
- package/src/assets/authentication.svg +1 -0
- package/src/assets/canvas-backdrop-0.png +0 -0
- package/src/assets/canvas-backdrop-1.png +0 -0
- package/src/assets/canvas-backdrop-2.png +0 -0
- package/src/assets/canvas-backdrop-3.png +0 -0
- package/src/assets/canvas-backdrop-4.png +0 -0
- package/src/assets/canvas-backdrop-5.png +0 -0
- package/src/assets/canvas-backdrop.png +0 -0
- package/src/assets/checkmark-animation.gif +0 -0
- package/src/assets/checkmark-animation.mp4 +0 -0
- package/src/assets/code-formatting/format-black.svg +6 -0
- package/src/assets/code-formatting/format-dark-grey.svg +6 -0
- package/src/assets/code-formatting/format-light-grey.svg +6 -0
- package/src/assets/code-formatting/format-white.svg +6 -0
- package/src/assets/code-formatting/inline-black.svg +5 -0
- package/src/assets/code-formatting/inline-dark-grey.svg +5 -0
- package/src/assets/code-formatting/inline-light-grey.svg +5 -0
- package/src/assets/code-formatting/inline-white.svg +5 -0
- package/src/assets/contained-logo-full-word.png +0 -0
- package/src/assets/cron-job-color.png +0 -0
- package/src/assets/cron-job.png +0 -0
- package/src/assets/database-table-color.png +0 -0
- package/src/assets/database-table.png +0 -0
- package/src/assets/datatype-icons/black/any.svg +1 -0
- package/src/assets/datatype-icons/black/binary.svg +1 -0
- package/src/assets/datatype-icons/black/boolean.svg +3 -0
- package/src/assets/datatype-icons/black/date-time.svg +3 -0
- package/src/assets/datatype-icons/black/definition-entity.svg +6 -0
- package/src/assets/datatype-icons/black/key-file.svg +1 -0
- package/src/assets/datatype-icons/black/list.svg +3 -0
- package/src/assets/datatype-icons/black/null.svg +3 -0
- package/src/assets/datatype-icons/black/number.svg +13 -0
- package/src/assets/datatype-icons/black/project.svg +12 -0
- package/src/assets/datatype-icons/black/sql-program.svg +2 -0
- package/src/assets/datatype-icons/black/text.svg +3 -0
- package/src/assets/datatype-icons/black/unknown.svg +3 -0
- package/src/assets/datatype-icons/black/uuid.svg +4 -0
- package/src/assets/datatype-icons/black/void.svg +1 -0
- package/src/assets/datatype-icons/dark-grey/any.svg +1 -0
- package/src/assets/datatype-icons/dark-grey/boolean.svg +3 -0
- package/src/assets/datatype-icons/dark-grey/date-time.svg +3 -0
- package/src/assets/datatype-icons/dark-grey/definition-entity.svg +6 -0
- package/src/assets/datatype-icons/dark-grey/list.svg +3 -0
- package/src/assets/datatype-icons/dark-grey/null.svg +3 -0
- package/src/assets/datatype-icons/dark-grey/number.svg +13 -0
- package/src/assets/datatype-icons/dark-grey/project.svg +12 -0
- package/src/assets/datatype-icons/dark-grey/sql-program.svg +2 -0
- package/src/assets/datatype-icons/dark-grey/text.svg +3 -0
- package/src/assets/datatype-icons/dark-grey/unknown.svg +3 -0
- package/src/assets/datatype-icons/dark-grey/uuid.svg +4 -0
- package/src/assets/datatype-icons/dark-grey/void.svg +1 -0
- package/src/assets/datatype-icons/light-grey/any.svg +1 -0
- package/src/assets/datatype-icons/light-grey/boolean.svg +3 -0
- package/src/assets/datatype-icons/light-grey/date-time.svg +3 -0
- package/src/assets/datatype-icons/light-grey/definition-entity.svg +6 -0
- package/src/assets/datatype-icons/light-grey/list.svg +3 -0
- package/src/assets/datatype-icons/light-grey/null.svg +3 -0
- package/src/assets/datatype-icons/light-grey/number.svg +13 -0
- package/src/assets/datatype-icons/light-grey/project.svg +12 -0
- package/src/assets/datatype-icons/light-grey/sql-program.svg +2 -0
- package/src/assets/datatype-icons/light-grey/text.svg +3 -0
- package/src/assets/datatype-icons/light-grey/unknown.svg +3 -0
- package/src/assets/datatype-icons/light-grey/uuid.svg +4 -0
- package/src/assets/datatype-icons/light-grey/void.svg +1 -0
- package/src/assets/edit.png +0 -0
- package/src/assets/execution.svg +13 -0
- package/src/assets/favicon.svg +14 -0
- package/src/assets/file-search.svg +1 -0
- package/src/assets/http-endpoint.png +0 -0
- package/src/assets/image-input-placeholder.png +0 -0
- package/src/assets/logo-full-word-white.png +0 -0
- package/src/assets/logo-full-word.png +0 -0
- package/src/assets/password.svg +85 -0
- package/src/assets/pencil.png +0 -0
- package/src/assets/publish-project-rich-icon-2.svg +1 -0
- package/src/assets/publish-project-rich-icon.svg +1 -0
- package/src/assets/relational-database.png +0 -0
- package/src/assets/resources.svg +3 -0
- package/src/assets/resume-icon-14px.png +0 -0
- package/src/assets/server.png +0 -0
- package/src/assets/small-status/checkmark.svg +4 -0
- package/src/assets/small-status/error.svg +4 -0
- package/src/assets/small-status/loading.svg +4 -0
- package/src/assets/small-status/skipped.svg +11 -0
- package/src/assets/sql-connection-config.svg +1 -0
- package/src/assets/sql-row-transformer.svg +1 -0
- package/src/assets/ssl-certificate-config.svg +1 -0
- package/src/assets/sync.svg +1 -0
- package/src/assets/testing-logic-icon.svg +1 -0
- package/src/assets/versions.svg +25 -0
- package/src/assets/visual-programming-icon.svg +1 -0
- package/src/assets/warning-sign-24px.png +0 -0
- package/src/auth/index.ts +318 -0
- package/src/components/DialogLoader.tsx +94 -0
- package/src/components/EntityDialogHeader.tsx +110 -0
- package/src/components/EntityDialogSectionHeader.tsx +214 -0
- package/src/components/GalleryAddExternalIntegrationInfoDialog.tsx +87 -0
- package/src/components/GenerateProjectStartingLogicPromptDialog.tsx +281 -0
- package/src/components/LegacyRouteRedirector.tsx +55 -0
- package/src/components/ProPlanChip.tsx +23 -0
- package/src/components/ReportBugDialog.tsx +412 -0
- package/src/components/RequestIntegrationAccessDialog.tsx +261 -0
- package/src/components/UseTemplateProjectDialog.tsx +193 -0
- package/src/components/WorkspaceLayout.tsx +152 -0
- package/src/components/animated-svg/AnimatedCheckmark.tsx +41 -0
- package/src/components/animated-svg/AnimatedCrossmark.tsx +51 -0
- package/src/components/animated-svg/AnimatedEmailSending.tsx +38 -0
- package/src/components/animated-svg/AnimatedLoading.tsx +72 -0
- package/src/components/animated-svg/animated-svg.css +239 -0
- package/src/components/canvas/Canvas.tsx +16 -0
- package/src/components/canvas/CreateEntityMenu.tsx +2020 -0
- package/src/components/canvas/canvas.css +10 -0
- package/src/components/canvas/create-entity-menu.css +579 -0
- package/src/components/canvas-search/CanvasSearch.tsx +501 -0
- package/src/components/canvas-search/canvas-search.css +126 -0
- package/src/components/canvas-settings-menu/CanvasSettingsMenuButton.tsx +515 -0
- package/src/components/canvas-settings-menu/canvas-settings-menu.css +96 -0
- package/src/components/circular-image-upload/CircularImageUpload.tsx +113 -0
- package/src/components/circular-image-upload/circular-image-upload.css +69 -0
- package/src/components/costs/CostsDialog.tsx +459 -0
- package/src/components/data-type/DataTypeBuilder.tsx +3127 -0
- package/src/components/data-type/data-type-builder.css +45 -0
- package/src/components/dialogs/BetaAcknowledgeDialog.tsx +43 -0
- package/src/components/dialogs/ComplexDataDialog.tsx +458 -0
- package/src/components/dialogs/CronBuilderDialog.tsx +2145 -0
- package/src/components/dialogs/ExternalIntegrationConnections.tsx +565 -0
- package/src/components/dialogs/JsonEditorDialog.tsx +1392 -0
- package/src/components/dialogs/StringEditorDialog.tsx +268 -0
- package/src/components/dialogs/argument-declaration/ArgumentDeclaration.tsx +1167 -0
- package/src/components/dialogs/argument-declaration/ArgumentDeclarationDialogContent.tsx +128 -0
- package/src/components/dialogs/beta-dialog.css +165 -0
- package/src/components/dialogs/condition/Condition.tsx +431 -0
- package/src/components/dialogs/condition/ConditionDialogContent.tsx +126 -0
- package/src/components/dialogs/definition-entity/DefinitionEntityDialogContent.tsx +973 -0
- package/src/components/dialogs/function-call/FunctionCall.tsx +442 -0
- package/src/components/dialogs/function-call/FunctionCallDialogContent.tsx +126 -0
- package/src/components/dialogs/function-declaration/FunctionDeclaration.tsx +926 -0
- package/src/components/dialogs/function-declaration/FunctionDeclarationDialogContent.tsx +124 -0
- package/src/components/dialogs/generating-project-starting-logic-overlay/GeneratingProjectStartingLogicOverlay.tsx +176 -0
- package/src/components/dialogs/generating-project-starting-logic-overlay/generating-project-starting-logic-overlay.css +13 -0
- package/src/components/dialogs/global-event/GlobalEvent.tsx +475 -0
- package/src/components/dialogs/global-event/GlobalEventDialogContent.tsx +126 -0
- package/src/components/dialogs/help/HelpDialog.tsx +217 -0
- package/src/components/dialogs/help/HelpDilalogHomeContent.tsx +178 -0
- package/src/components/dialogs/help/help-dialog.css +116 -0
- package/src/components/dialogs/help/help-icon/HelpIconButton.tsx +41 -0
- package/src/components/dialogs/help/help-icon/help-icon.css +9 -0
- package/src/components/dialogs/input-map/InputMap.tsx +635 -0
- package/src/components/dialogs/input-map/InputMapDialogContent.tsx +126 -0
- package/src/components/dialogs/json-editor-dialog.css +4 -0
- package/src/components/dialogs/loop/Loop.tsx +650 -0
- package/src/components/dialogs/loop/LoopDialogContent.tsx +122 -0
- package/src/components/dialogs/operation/Operation.tsx +440 -0
- package/src/components/dialogs/operation/OperationDialogContent.tsx +126 -0
- package/src/components/dialogs/output-map/OutputMap.tsx +536 -0
- package/src/components/dialogs/output-map/OutputMapDialogContent.tsx +126 -0
- package/src/components/dialogs/property/Property.tsx +1490 -0
- package/src/components/dialogs/property/PropertyDialogContent.tsx +106 -0
- package/src/components/dialogs/search-statement/ColumnSelector.tsx +334 -0
- package/src/components/dialogs/search-statement/ConditionBuilder.tsx +750 -0
- package/src/components/dialogs/search-statement/DataAggregationSection.tsx +621 -0
- package/src/components/dialogs/search-statement/DataSourceSelection.tsx +734 -0
- package/src/components/dialogs/search-statement/EntityMetadataSection.tsx +135 -0
- package/src/components/dialogs/search-statement/FilterConditionsSection.tsx +151 -0
- package/src/components/dialogs/search-statement/InlineInputMap.tsx +153 -0
- package/src/components/dialogs/search-statement/LiteralValue.tsx +616 -0
- package/src/components/dialogs/search-statement/MainSourceAndInputsSection.tsx +271 -0
- package/src/components/dialogs/search-statement/NestedSearchStatementBuilder.tsx +170 -0
- package/src/components/dialogs/search-statement/OutputFormatSection.tsx +1779 -0
- package/src/components/dialogs/search-statement/ResultsSection.tsx +344 -0
- package/src/components/dialogs/search-statement/SearchStatementBuilder.tsx +251 -0
- package/src/components/dialogs/search-statement/SearchStatementDialogContent.tsx +398 -0
- package/src/components/dialogs/search-statement/ValueSelector.tsx +766 -0
- package/src/components/dialogs/search-statement/search-statement-context.tsx +1630 -0
- package/src/components/dialogs/search-statement/search-statement-dialog.css +56 -0
- package/src/components/dialogs/search-statement/test.sql +111 -0
- package/src/components/dialogs/value-descriptor/ValueDescriptor.tsx +824 -0
- package/src/components/dialogs/value-descriptor/ValueDescriptorDialogContent.tsx +124 -0
- package/src/components/dialogs/variable-declaration/VariableDeclaration.tsx +836 -0
- package/src/components/dialogs/variable-declaration/VariableDeclarationDialogContent.tsx +106 -0
- package/src/components/dialogs/variable-instance/VariableInstance.tsx +443 -0
- package/src/components/dialogs/variable-instance/VariableInstanceDialogContent.tsx +124 -0
- package/src/components/draggable-entity-card/ArgumentDeclaration.tsx +736 -0
- package/src/components/draggable-entity-card/CollapseEntityButton.tsx +170 -0
- package/src/components/draggable-entity-card/ConditionCard.tsx +1062 -0
- package/src/components/draggable-entity-card/ConnectionDeleteButton.tsx +309 -0
- package/src/components/draggable-entity-card/DataTypeIcon.tsx +624 -0
- package/src/components/draggable-entity-card/DraggableEntityCard.tsx +617 -0
- package/src/components/draggable-entity-card/ErrorMapProperty.tsx +464 -0
- package/src/components/draggable-entity-card/EventCard.tsx +700 -0
- package/src/components/draggable-entity-card/ExecutionInProgressValue.tsx +327 -0
- package/src/components/draggable-entity-card/FunctionDeclarationCard.tsx +819 -0
- package/src/components/draggable-entity-card/InputMapProperty.tsx +1067 -0
- package/src/components/draggable-entity-card/InternalCall.tsx +978 -0
- package/src/components/draggable-entity-card/InternalCallExecutionNode.tsx +643 -0
- package/src/components/draggable-entity-card/LogicScopeCallerNode.tsx +262 -0
- package/src/components/draggable-entity-card/LoopCard.tsx +791 -0
- package/src/components/draggable-entity-card/MainValueInput.tsx +523 -0
- package/src/components/draggable-entity-card/MainValueOutput.tsx +458 -0
- package/src/components/draggable-entity-card/MethodDeclaration.tsx +1088 -0
- package/src/components/draggable-entity-card/NestedCondition.tsx +1025 -0
- package/src/components/draggable-entity-card/OutputMapProperty.tsx +843 -0
- package/src/components/draggable-entity-card/PassthroughEntityCard.tsx +1247 -0
- package/src/components/draggable-entity-card/ReturnedError.tsx +549 -0
- package/src/components/draggable-entity-card/SmallSuccessFailureNodes.tsx +523 -0
- package/src/components/draggable-entity-card/SuccessFailureNodes.tsx +509 -0
- package/src/components/draggable-entity-card/TestEntityButton.tsx +946 -0
- package/src/components/draggable-entity-card/TestMenu.tsx +523 -0
- package/src/components/draggable-entity-card/TestMenuValidationDropdown.tsx +84 -0
- package/src/components/draggable-entity-card/UnreachableMarker.tsx +114 -0
- package/src/components/draggable-entity-card/VariableCard.tsx +1577 -0
- package/src/components/draggable-entity-card/VariableScopeMarker.tsx +117 -0
- package/src/components/draggable-entity-card/collapse-entity-button.css +44 -0
- package/src/components/draggable-entity-card/definition-entity/DefinitionEntityCard.tsx +1181 -0
- package/src/components/draggable-entity-card/definition-entity/DefinitionEntityIcon.tsx +36 -0
- package/src/components/draggable-entity-card/definition-entity/DefinitionEntityProperty.tsx +478 -0
- package/src/components/draggable-entity-card/definition-entity/DynamicFooterActions.tsx +112 -0
- package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/ExportCredentialsFooterAction.tsx +461 -0
- package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/RestablishConnectionFooterAction.tsx +199 -0
- package/src/components/draggable-entity-card/definition-entity/actions/external-integration-connection/restablish-connection-footer-action.css +85 -0
- package/src/components/draggable-entity-card/definition-entity/actions/google-drive/GoogleDriveFilePickerAPIFooterAction.tsx +277 -0
- package/src/components/draggable-entity-card/definition-entity/actions/google-drive/google-drive-file-picker-api-footer-action.css +107 -0
- package/src/components/draggable-entity-card/definition-entity/actions/persisted-entity/DatabaseFooterAction.tsx +452 -0
- package/src/components/draggable-entity-card/definition-entity/actions/persisted-entity/database-footer-action.css +86 -0
- package/src/components/draggable-entity-card/definition-entity/definition-entity-card.css +17 -0
- package/src/components/draggable-entity-card/draggable-entity-card.css +1140 -0
- package/src/components/draggable-entity-card/entity-locked-icon/EntityLockedIcon.tsx +133 -0
- package/src/components/draggable-entity-card/entity-locked-icon/entity-locked.css +8 -0
- package/src/components/draggable-entity-card/expand-properties-icon-button/ExpandPropertiesIconButton.tsx +84 -0
- package/src/components/draggable-entity-card/expand-properties-icon-button/expand-properties-icon-button.css +21 -0
- package/src/components/draggable-entity-card/implement-entity-icon/ImplementEntityIcon.tsx +74 -0
- package/src/components/draggable-entity-card/implement-entity-icon/implement-entity-icon.css +13 -0
- package/src/components/draggable-entity-card/logic-error/LogicErrorIconMenu.tsx +424 -0
- package/src/components/draggable-entity-card/logic-error/logic-error.css +23 -0
- package/src/components/draggable-entity-card/new-card-input-button/NewCardInputButton.tsx +193 -0
- package/src/components/draggable-entity-card/new-card-input-button/NewDynamicInputButton.tsx +214 -0
- package/src/components/draggable-entity-card/new-card-input-button/new-card-input-button.css +71 -0
- package/src/components/draggable-entity-card/new-card-output-button/NewCardOutputButton.tsx +192 -0
- package/src/components/draggable-entity-card/new-card-output-button/new-card-output-button.css +71 -0
- package/src/components/draggable-entity-card/termination-statement/TerminationStatementCard.tsx +1543 -0
- package/src/components/draggable-entity-card/termination-statement/termination-statement-card.css +17 -0
- package/src/components/draggable-entity-card/test-entity-button.css +55 -0
- package/src/components/draggable-entity-card/test-menu.css +181 -0
- package/src/components/draggable-entity-card/unreachable-marker.css +43 -0
- package/src/components/draggable-entity-card/variable-scope-marker.css +22 -0
- package/src/components/dynamic-value/DynamicValue.tsx +2395 -0
- package/src/components/dynamic-value/DynamicValueEntry.tsx +1957 -0
- package/src/components/dynamic-value/dynamic-value.css +230 -0
- package/src/components/editor/ElyxMonacoEditor.tsx +38 -0
- package/src/components/entity-error/EntityErrorListItem.tsx +47 -0
- package/src/components/entity-error/entity-error.css +198 -0
- package/src/components/entity-icon/EntityIcon.tsx +292 -0
- package/src/components/entity-icon/entity-icon.css +39 -0
- package/src/components/gallery-card/CreateNewProject.tsx +222 -0
- package/src/components/gallery-card/GalleryCard.tsx +171 -0
- package/src/components/gallery-card/MarketplaceCard.tsx +87 -0
- package/src/components/gallery-card/ProjectDuplicationCard.tsx +575 -0
- package/src/components/gallery-card/gallery-card.css +25 -0
- package/src/components/notifications/NotificationsIconButton.tsx +124 -0
- package/src/components/notifications/NotificationsPanel.tsx +385 -0
- package/src/components/notifications/notifications.css +189 -0
- package/src/components/online-users/LocalOnlineUsers.tsx +175 -0
- package/src/components/online-users/PageOnlineUsers.tsx +297 -0
- package/src/components/online-users/online-users.css +72 -0
- package/src/components/page-backdrop/PageBackdrop.tsx +8 -0
- package/src/components/page-backdrop/page-backdrop.css +7 -0
- package/src/components/project-configuration/DeleteProjectConfirmationDialog.tsx +134 -0
- package/src/components/project-configuration/ProjectConfigurationDialog.tsx +972 -0
- package/src/components/project-configuration/ProjectDataForm.tsx +121 -0
- package/src/components/project-configuration/UnpublishProjectConfirmationDialog.tsx +162 -0
- package/src/components/project-configuration/project-configuration-content.css +209 -0
- package/src/components/project-name/ProjectName.tsx +2025 -0
- package/src/components/project-name/project-name.css +599 -0
- package/src/components/publishing/Publication.tsx +133 -0
- package/src/components/publishing/history/PublicationHistoryContent.tsx +414 -0
- package/src/components/publishing/history/PublicationHistoryDialog.tsx +234 -0
- package/src/components/publishing/preview/PublicationPreviewDialog.tsx +1158 -0
- package/src/components/publishing/preview/PublishingPriceForecast.tsx +160 -0
- package/src/components/publishing/preview/PublishingResourcesDetails.tsx +91 -0
- package/src/components/publishing/publication-sequence/PublishingSequenceContent.tsx +375 -0
- package/src/components/publishing/publication-sequence/PublishingSequenceDialog.tsx +344 -0
- package/src/components/publishing/publishing-dialog.css +142 -0
- package/src/components/publishing/utils.ts +227 -0
- package/src/components/resources/ResourcesDialog.tsx +591 -0
- package/src/components/resources/UpgradeBanner.tsx +102 -0
- package/src/components/resources/codebase/CodebaseDetails.tsx +156 -0
- package/src/components/resources/cron-job/CronJobsList.tsx +532 -0
- package/src/components/resources/functions/FunctionsList.tsx +454 -0
- package/src/components/resources/http-api/HttpAPI.tsx +566 -0
- package/src/components/resources/http-api/HttpAPIClientModule.tsx +37 -0
- package/src/components/resources/logs/LogsViewer.tsx +768 -0
- package/src/components/resources/query.ts +74 -0
- package/src/components/resources/relational-database/DatabaseTable.tsx +905 -0
- package/src/components/resources/relational-database/RelationalDatabase.tsx +83 -0
- package/src/components/resources/relational-database/RelationalDatabaseSecrets.tsx +361 -0
- package/src/components/resources/resources-dialog.css +74 -0
- package/src/components/test-relational-database/DatabaseTable.tsx +913 -0
- package/src/components/test-relational-database/TestDatabaseDialogContent.tsx +670 -0
- package/src/components/test-relational-database/query.ts +74 -0
- package/src/components/toolbar/ToolBar.tsx +236 -0
- package/src/components/toolbar/toolbar.css +78 -0
- package/src/components/transaction-history/TransactionHistoryDialog.tsx +268 -0
- package/src/components/user/CurrentUserAvatar.tsx +65 -0
- package/src/components/user/UserChip.tsx +62 -0
- package/src/components/user/user.css +39 -0
- package/src/components/user-profile/ChangePasswordForm.tsx +67 -0
- package/src/components/user-profile/OwnUserProfileContent.tsx +665 -0
- package/src/components/user-profile/PublicUserProfileContent.tsx +99 -0
- package/src/components/user-profile/UserDataForm.tsx +75 -0
- package/src/components/user-profile/UserProfileDialog.tsx +110 -0
- package/src/components/user-profile/user-profile-content.css +25 -0
- package/src/config.ts +130 -0
- package/src/globals.d.ts +13 -0
- package/src/index.html +27 -0
- package/src/index.tsx +23 -0
- package/src/lib/badge/Badge.tsx +35 -0
- package/src/lib/badge/badge.css +32 -0
- package/src/lib/button/Button.tsx +129 -0
- package/src/lib/button/button.css +145 -0
- package/src/lib/canvas/canvas-undo-redo.ts +263 -0
- package/src/lib/canvas/defs.ts +170 -0
- package/src/lib/canvas/index.test.ts +189 -0
- package/src/lib/canvas/index.ts +6999 -0
- package/src/lib/canvas/utils.ts +59 -0
- package/src/lib/card/Card.tsx +62 -0
- package/src/lib/card/LoadingCard.tsx +82 -0
- package/src/lib/card/card.css +259 -0
- package/src/lib/chip/Chip.tsx +79 -0
- package/src/lib/chip/chip.css +0 -0
- package/src/lib/dialog/Dialog.tsx +122 -0
- package/src/lib/dialog/SmallDialog.tsx +61 -0
- package/src/lib/dialog/dialog.css +40 -0
- package/src/lib/display-data-structure/index.tsx +21 -0
- package/src/lib/dropdown/CanvasDropdownMenuCard.tsx +68 -0
- package/src/lib/dropdown/CanvasDropdownMenuCardOption.tsx +136 -0
- package/src/lib/dropdown/DropdownButton.tsx +104 -0
- package/src/lib/dropdown/DropdownMenuCard.tsx +324 -0
- package/src/lib/dropdown/DropdownMenuPopup.tsx +27 -0
- package/src/lib/dropdown/dropdown-button.css +76 -0
- package/src/lib/dropdown/dropdown-menu.css +151 -0
- package/src/lib/json-editor/RawJsonEditor.tsx +137 -0
- package/src/lib/json-editor/json-editor.css +35 -0
- package/src/lib/loader/Loader.tsx +120 -0
- package/src/lib/loader/loader.css +38 -0
- package/src/lib/pagination/Pagination.tsx +64 -0
- package/src/lib/popup/CanvasPopupBaseComponent.tsx +103 -0
- package/src/lib/popup/Popup.tsx +243 -0
- package/src/lib/popup/popup.css +16 -0
- package/src/lib/table/RowForm.tsx +301 -0
- package/src/lib/table/Table.tsx +1069 -0
- package/src/lib/table/table.css +249 -0
- package/src/lib/table/types.ts +108 -0
- package/src/lib/text-area/TextArea.tsx +183 -0
- package/src/lib/text-area/text-area.css +156 -0
- package/src/lib/text-field/TextField.tsx +218 -0
- package/src/lib/text-field/index.ts +8 -0
- package/src/lib/text-field/text-field.css +201 -0
- package/src/lib/tooltip/Tooltip.tsx +24 -0
- package/src/lib/tooltip/tooltip.css +17 -0
- package/src/localization/index.ts +47 -0
- package/src/main.css +343 -0
- package/src/pages/Auth.tsx +848 -0
- package/src/pages/Editor.tsx +883 -0
- package/src/pages/ErrorPage.tsx +179 -0
- package/src/pages/Gallery.tsx +1693 -0
- package/src/pages/NewPaymentMethodCallback.tsx +53 -0
- package/src/pages/NotFoundPage.tsx +126 -0
- package/src/pages/PricingPlans.tsx +155 -0
- package/src/pages/auth.css +304 -0
- package/src/pages/gallery.css +421 -0
- package/src/payments/index.ts +187 -0
- package/src/popup-notification/index.ts +90 -0
- package/src/services/database/index.ts +1 -0
- package/src/services/database/utils.ts +1301 -0
- package/src/services/editor/CanvasElement.tsx +2934 -0
- package/src/services/editor/CanvasElementConnectionDeleteButton.ts +204 -0
- package/src/services/editor/CanvasPopup.tsx +749 -0
- package/src/services/editor/EditorService.ts +8157 -0
- package/src/services/editor/area.ts +1312 -0
- package/src/services/editor/connections.ts +1019 -0
- package/src/services/editor/create/condition.ts +25 -0
- package/src/services/editor/create/definition-entity.ts +29 -0
- package/src/services/editor/create/function-call.ts +25 -0
- package/src/services/editor/create/global-event.ts +33 -0
- package/src/services/editor/create/loop.ts +25 -0
- package/src/services/editor/create/operation.ts +30 -0
- package/src/services/editor/create/utils.ts +140 -0
- package/src/services/editor/create/variable-declaration.ts +135 -0
- package/src/services/editor/create/variable-instance.ts +100 -0
- package/src/services/editor/editor-ui-extensions-context.ts +43 -0
- package/src/services/editor/entities-metadata.json +9310 -0
- package/src/services/editor/icons.ts +1093 -0
- package/src/services/editor/index.ts +1 -0
- package/src/services/editor/layout.ts +102 -0
- package/src/services/editor/modules/built-in-function-implementations/base.ts +14 -0
- package/src/services/editor/modules/built-in-function-implementations/create-persisted-entity/index.ts +56 -0
- package/src/services/editor/modules/built-in-function-implementations/delete-persisted-entity/index.ts +55 -0
- package/src/services/editor/modules/built-in-function-implementations/index.ts +4 -0
- package/src/services/editor/modules/built-in-function-implementations/update-persisted-entity/index.ts +56 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/get-files.ts +183 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/list-drives.ts +124 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/list-root-folders.ts +125 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/smart-fetch-document.ts +702 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-drive/upload-document.ts +535 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-gemini/generate-content.ts +193 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-mail/get-emails.ts +586 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/google-mail/send-email.ts +386 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/index.ts +12 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/slack/channels.ts +240 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/slack/messages.ts +210 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/slack/replies.ts +200 -0
- package/src/services/editor/modules/operations-implementations/external-integrations/slack/send-message.ts +177 -0
- package/src/services/editor/modules/operations-implementations/index.ts +1 -0
- package/src/services/editor/modules/search-node-implementation/index.ts +42 -0
- package/src/services/editor/modules/sql-migrations-generation.tsx +1054 -0
- package/src/services/editor/publication/publication.ts +578 -0
- package/src/services/editor/ui.ts +1348 -0
- package/src/services/editor/utils.ts +5868 -0
- package/src/services/editor/value-store.ts +619 -0
- package/src/services/execution/built-in-function-implementations.ts +422 -0
- package/src/services/execution/index.ts +4747 -0
- package/src/services/execution/logic.ts +121 -0
- package/src/services/execution/test-instance.tsx +2296 -0
- package/src/services/execution/utils.ts +33 -0
- package/src/services/execution/value-resolution.test.ts +424 -0
- package/src/services/execution/value-resolution.ts +4087 -0
- package/src/services/integrations/ExternalIntegrationsService.ts +439 -0
- package/src/services/integrations/api.ts +175 -0
- package/src/services/local-relational-database/idb_helper.ts +66 -0
- package/src/services/local-relational-database/index.ts +3308 -0
- package/src/services/local-relational-database/utils.ts +403 -0
- package/src/services/notifications/index.ts +525 -0
- package/src/services/user/index.ts +144 -0
- package/src/setupTests.ts +1 -0
- package/src/socket/socket.ts +248 -0
- package/src/socket/utils.ts +10 -0
- package/src/store/workspace.ts +12 -0
- package/src/theme.ts +19 -0
- package/src/utils/DOM.ts +39 -0
- package/src/utils/date.ts +169 -0
- package/src/utils/index.ts +158 -0
- package/src/utils/react.tsx +679 -0
- package/src/utils/testing.ts +103 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
// browser-socket.ts
|
|
2
|
+
import {
|
|
3
|
+
Events,
|
|
4
|
+
IWebSocketCommon,
|
|
5
|
+
IWebsocketsClient,
|
|
6
|
+
Logger,
|
|
7
|
+
} from '@elyx-code/common-ts-utils';
|
|
8
|
+
import { parseRTCMessage } from './utils';
|
|
9
|
+
import { ILiveMessageDTO } from '@elyx-code/definitions';
|
|
10
|
+
|
|
11
|
+
type BrowserSocketOptions = {
|
|
12
|
+
socketUrl: string;
|
|
13
|
+
maxQueue?: number; // default 1000
|
|
14
|
+
connectTimeoutMs?: number; // default 10_000
|
|
15
|
+
heartbeatMs?: number; // default 30_000
|
|
16
|
+
idleRestartMs?: number; // default 120_000
|
|
17
|
+
backoffInitialMs?: number; // default 500
|
|
18
|
+
backoffMaxMs?: number; // default 15_000
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export class Socket extends Events implements IWebsocketsClient {
|
|
22
|
+
public connection!: IWebSocketCommon;
|
|
23
|
+
|
|
24
|
+
private url: string;
|
|
25
|
+
private queue: ILiveMessageDTO[] = [];
|
|
26
|
+
|
|
27
|
+
private reconnectAttempts = 0;
|
|
28
|
+
private manualClose = false;
|
|
29
|
+
private connectTimer?: number;
|
|
30
|
+
private heartbeatTimer?: number;
|
|
31
|
+
private idleTimer?: number;
|
|
32
|
+
private lastSeenAt = Date.now();
|
|
33
|
+
|
|
34
|
+
private readonly MAX_QUEUE: number;
|
|
35
|
+
private readonly CONNECT_TIMEOUT_MS: number;
|
|
36
|
+
private readonly HEARTBEAT_MS: number;
|
|
37
|
+
private readonly IDLE_RESTART_MS: number;
|
|
38
|
+
private readonly BACKOFF_INITIAL_MS: number;
|
|
39
|
+
private readonly BACKOFF_MAX_MS: number;
|
|
40
|
+
|
|
41
|
+
constructor(options: BrowserSocketOptions) {
|
|
42
|
+
super();
|
|
43
|
+
|
|
44
|
+
this.url = options.socketUrl;
|
|
45
|
+
this.MAX_QUEUE = options.maxQueue ?? 1000;
|
|
46
|
+
this.CONNECT_TIMEOUT_MS = options.connectTimeoutMs ?? 10_000;
|
|
47
|
+
this.HEARTBEAT_MS = options.heartbeatMs ?? 30_000;
|
|
48
|
+
this.IDLE_RESTART_MS = options.idleRestartMs ?? 120_000;
|
|
49
|
+
this.BACKOFF_INITIAL_MS = options.backoffInitialMs ?? 500;
|
|
50
|
+
this.BACKOFF_MAX_MS = options.backoffMaxMs ?? 15_000;
|
|
51
|
+
|
|
52
|
+
this.open();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---- lifecycle -----------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
private open() {
|
|
58
|
+
const ws = new WebSocket(this.url);
|
|
59
|
+
|
|
60
|
+
// patch close() so external callers (socket.connection.close()) stop reconnection
|
|
61
|
+
const originalClose = ws.close.bind(ws);
|
|
62
|
+
ws.close = (code?: number, reason?: string) => {
|
|
63
|
+
// this.manualClose = true;
|
|
64
|
+
return originalClose(code, reason);
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
this.connection = ws as any as IWebSocketCommon;
|
|
68
|
+
|
|
69
|
+
// connect-timeout guard
|
|
70
|
+
this.clearTimer(this.connectTimer);
|
|
71
|
+
this.connectTimer = window.setTimeout(() => {
|
|
72
|
+
if (
|
|
73
|
+
ws.readyState !== WebSocket.OPEN &&
|
|
74
|
+
ws.readyState !== WebSocket.CLOSED
|
|
75
|
+
) {
|
|
76
|
+
try {
|
|
77
|
+
ws.close(4000, 'connect-timeout');
|
|
78
|
+
} catch {}
|
|
79
|
+
}
|
|
80
|
+
}, this.CONNECT_TIMEOUT_MS);
|
|
81
|
+
|
|
82
|
+
// listeners — attach first to avoid missing early events
|
|
83
|
+
ws.addEventListener('open', this.onOpen as any);
|
|
84
|
+
ws.addEventListener('message', this.onMessageEvent as any);
|
|
85
|
+
ws.addEventListener('error', this.onError as any);
|
|
86
|
+
ws.addEventListener('close', this.onClose as any);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
private onOpen = () => {
|
|
90
|
+
this.reconnectAttempts = 0;
|
|
91
|
+
this.lastSeenAt = Date.now();
|
|
92
|
+
|
|
93
|
+
// flush queue
|
|
94
|
+
const toSend = this.queue.splice(0, this.queue.length);
|
|
95
|
+
for (const m of toSend) this._send(m);
|
|
96
|
+
|
|
97
|
+
this.startHeartbeat();
|
|
98
|
+
this.startIdleWatch();
|
|
99
|
+
|
|
100
|
+
this.emit('open', null);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
private onMessageEvent = async (e: MessageEvent) => {
|
|
104
|
+
this.lastSeenAt = Date.now();
|
|
105
|
+
const data = e.data;
|
|
106
|
+
|
|
107
|
+
const messageStr =
|
|
108
|
+
typeof data === 'string'
|
|
109
|
+
? data
|
|
110
|
+
: data instanceof ArrayBuffer
|
|
111
|
+
? new TextDecoder().decode(data)
|
|
112
|
+
: data instanceof Blob
|
|
113
|
+
? await data.text()
|
|
114
|
+
: '';
|
|
115
|
+
|
|
116
|
+
const parsedMessage: ILiveMessageDTO | string = parseRTCMessage(messageStr);
|
|
117
|
+
this.emit('message', parsedMessage);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
private onError = (event: Event) => {
|
|
121
|
+
this.emit('error', event);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
private onClose = (evt: CloseEvent) => {
|
|
125
|
+
this.stopHeartbeat();
|
|
126
|
+
this.stopIdleWatch();
|
|
127
|
+
this.clearTimer(this.connectTimer);
|
|
128
|
+
|
|
129
|
+
const code = evt.code;
|
|
130
|
+
const reason = (evt.reason ?? '').toString();
|
|
131
|
+
|
|
132
|
+
this.emit('closed', { code, reason });
|
|
133
|
+
|
|
134
|
+
if (this.manualClose) {
|
|
135
|
+
Logger.log('WebSocket closed manually, not reconnecting');
|
|
136
|
+
return
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
this.scheduleReconnect(`code=${code} reason=${reason}`);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// ---- resilience helpers --------------------------------------------------
|
|
143
|
+
|
|
144
|
+
private scheduleReconnect(debugReason: string) {
|
|
145
|
+
const base = Math.min(
|
|
146
|
+
this.BACKOFF_INITIAL_MS * 2 ** this.reconnectAttempts,
|
|
147
|
+
this.BACKOFF_MAX_MS
|
|
148
|
+
);
|
|
149
|
+
const jitter = Math.floor(Math.random() * 800);
|
|
150
|
+
const delay = base + jitter;
|
|
151
|
+
this.reconnectAttempts++;
|
|
152
|
+
|
|
153
|
+
Logger.log(`WebSocket disconnected, scheduling reconnect in: ${delay}ms ${debugReason}`);
|
|
154
|
+
|
|
155
|
+
window.setTimeout(() => this.open(), delay);
|
|
156
|
+
|
|
157
|
+
this.emit('reconnecting', {
|
|
158
|
+
attempt: this.reconnectAttempts,
|
|
159
|
+
delay,
|
|
160
|
+
reason: debugReason,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private startHeartbeat() {
|
|
165
|
+
this.stopHeartbeat();
|
|
166
|
+
this.heartbeatTimer = window.setInterval(() => {
|
|
167
|
+
// App-level ping; safe for API Gateway and browsers
|
|
168
|
+
try {
|
|
169
|
+
this.connection?.send?.(
|
|
170
|
+
JSON.stringify({ type: 'ping', t: Date.now() })
|
|
171
|
+
);
|
|
172
|
+
} catch {}
|
|
173
|
+
}, this.HEARTBEAT_MS);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private stopHeartbeat() {
|
|
177
|
+
this.clearTimer(this.heartbeatTimer);
|
|
178
|
+
this.heartbeatTimer = undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private startIdleWatch() {
|
|
182
|
+
this.stopIdleWatch();
|
|
183
|
+
this.idleTimer = window.setInterval(() => {
|
|
184
|
+
const idleFor = Date.now() - this.lastSeenAt;
|
|
185
|
+
if (idleFor >= this.IDLE_RESTART_MS) {
|
|
186
|
+
try {
|
|
187
|
+
this.connection?.close?.(4001, 'idle-restart');
|
|
188
|
+
} catch {}
|
|
189
|
+
}
|
|
190
|
+
}, Math.min(5_000, this.IDLE_RESTART_MS / 2));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
private stopIdleWatch() {
|
|
194
|
+
this.clearTimer(this.idleTimer);
|
|
195
|
+
this.idleTimer = undefined;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private clearTimer(t?: number) {
|
|
199
|
+
if (t) window.clearTimeout(t);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---- public send API -----------------------------------------------------
|
|
203
|
+
|
|
204
|
+
private _send(outgoingMessage: ILiveMessageDTO) {
|
|
205
|
+
if (!this.connection) {
|
|
206
|
+
if (this.queue.length >= this.MAX_QUEUE) {
|
|
207
|
+
this.emit('overflow', { dropped: 1, reason: 'no-connection' });
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
this.queue.push(outgoingMessage);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (this.connection.readyState === WebSocket.OPEN) {
|
|
215
|
+
try {
|
|
216
|
+
this.connection.send(JSON.stringify(outgoingMessage));
|
|
217
|
+
} catch (err) {
|
|
218
|
+
if (this.queue.length >= this.MAX_QUEUE) {
|
|
219
|
+
this.emit('overflow', { dropped: 1, reason: 'send-error' });
|
|
220
|
+
} else {
|
|
221
|
+
this.queue.push(outgoingMessage);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
if (this.queue.length >= this.MAX_QUEUE) {
|
|
226
|
+
this.emit('overflow', { dropped: 1, reason: 'not-open' });
|
|
227
|
+
} else {
|
|
228
|
+
this.queue.push(outgoingMessage);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public send(message: ILiveMessageDTO) {
|
|
234
|
+
// eslint-disable-next-line no-console
|
|
235
|
+
Logger.log('Sending websocket message:', message);
|
|
236
|
+
this._send(message);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Optional: call to stop auto-reconnect + close
|
|
240
|
+
public shutdown(code = 1000, reason = 'client closing') {
|
|
241
|
+
this.manualClose = true;
|
|
242
|
+
this.stopHeartbeat();
|
|
243
|
+
this.stopIdleWatch();
|
|
244
|
+
try {
|
|
245
|
+
this.connection?.close?.(code, reason);
|
|
246
|
+
} catch {}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { IWorkspace } from '@elyx-code/definitions';
|
|
2
|
+
import { create } from 'zustand';
|
|
3
|
+
|
|
4
|
+
interface WorkspaceStore {
|
|
5
|
+
activeWorkspace: IWorkspace | null;
|
|
6
|
+
setActiveWorkspace: (workspace: IWorkspace) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const useWorkspaceStore = create<WorkspaceStore>((set) => ({
|
|
10
|
+
activeWorkspace: null,
|
|
11
|
+
setActiveWorkspace: (workspace) => set({ activeWorkspace: workspace }),
|
|
12
|
+
}));
|
package/src/theme.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createTheme } from '@mui/material';
|
|
2
|
+
|
|
3
|
+
// Initialize mui theme
|
|
4
|
+
export const theme = createTheme({
|
|
5
|
+
palette: {
|
|
6
|
+
primary: {
|
|
7
|
+
light: '#rgb(139, 94, 255)',
|
|
8
|
+
main: '#6F36FF',
|
|
9
|
+
dark: '#rgb(77, 37, 178)',
|
|
10
|
+
contrastText: '#FFFFFF',
|
|
11
|
+
},
|
|
12
|
+
secondary: {
|
|
13
|
+
light: 'rgb(51, 51, 51)',
|
|
14
|
+
main: '#000000',
|
|
15
|
+
dark: '#000000',
|
|
16
|
+
contrastText: '#fff',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
});
|
package/src/utils/DOM.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function onClickAway(
|
|
2
|
+
element: Element,
|
|
3
|
+
callback: (e: Event) => void,
|
|
4
|
+
...targets: Element[]
|
|
5
|
+
) {
|
|
6
|
+
const listener = (event: Event) => {
|
|
7
|
+
if (!element?.contains(event.target as Node)) {
|
|
8
|
+
callback(event);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
if (!targets.length) {
|
|
14
|
+
document.addEventListener('click', listener);
|
|
15
|
+
} else {
|
|
16
|
+
targets.forEach((target) => {
|
|
17
|
+
if (!!target) {
|
|
18
|
+
target.addEventListener('click', listener);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return () => {
|
|
24
|
+
if (!targets.length) {
|
|
25
|
+
document.removeEventListener('click', listener);
|
|
26
|
+
} else {
|
|
27
|
+
targets.forEach((target) => {
|
|
28
|
+
if (!!target) {
|
|
29
|
+
target.removeEventListener('click', listener);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function hasAnimation(element: Element): boolean {
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
return getComputedStyle(element, null)['animation-name'] !== 'none';
|
|
39
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
export function toHtmlDate(date: Date) {
|
|
2
|
+
const day = ('0' + date.getDate()).slice(-2);
|
|
3
|
+
const month = ('0' + (date.getMonth() + 1)).slice(-2);
|
|
4
|
+
|
|
5
|
+
const formattedDate = date.getFullYear() + '-' + month + '-' + day;
|
|
6
|
+
|
|
7
|
+
return formattedDate;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function readableTimeFromNow(date: Date): string {
|
|
11
|
+
const now = new Date();
|
|
12
|
+
const diff = date.getTime() - now.getTime(); // difference in milliseconds
|
|
13
|
+
const absDiff = Math.abs(diff);
|
|
14
|
+
|
|
15
|
+
// Calculate time units
|
|
16
|
+
const seconds = Math.floor(absDiff / 1000);
|
|
17
|
+
const minutes = Math.floor(seconds / 60);
|
|
18
|
+
const hours = Math.floor(minutes / 60);
|
|
19
|
+
const days = Math.floor(hours / 24);
|
|
20
|
+
|
|
21
|
+
// Format the result
|
|
22
|
+
let result = '';
|
|
23
|
+
|
|
24
|
+
if (days > 0) {
|
|
25
|
+
result = `${days} day${days > 1 ? 's' : ''}`;
|
|
26
|
+
} else if (hours > 0) {
|
|
27
|
+
result = `${hours} hour${hours > 1 ? 's' : ''}`;
|
|
28
|
+
} else if (minutes > 0) {
|
|
29
|
+
result = `${minutes} min`;
|
|
30
|
+
} else if (seconds > 0) {
|
|
31
|
+
result = `${seconds} sec`;
|
|
32
|
+
} else {
|
|
33
|
+
// For cases where the difference is less than a second
|
|
34
|
+
return 'now';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Prefix with "in" for future dates, "ago" for past dates
|
|
38
|
+
return diff > 0 ? `in ${result}` : `${result} ago`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
export function getMonthRange(referenceDate: Date): {
|
|
43
|
+
startDate: Date;
|
|
44
|
+
endDate: Date;
|
|
45
|
+
} {
|
|
46
|
+
const year = referenceDate.getFullYear();
|
|
47
|
+
const month = referenceDate.getMonth();
|
|
48
|
+
const dayOfTheMonth = 1;
|
|
49
|
+
|
|
50
|
+
const timezoneMinutesOffset = new Date(
|
|
51
|
+
year,
|
|
52
|
+
month,
|
|
53
|
+
dayOfTheMonth
|
|
54
|
+
).getTimezoneOffset();
|
|
55
|
+
const timezoneHoursOffset = timezoneMinutesOffset / 60;
|
|
56
|
+
|
|
57
|
+
// We turn the current hours offset into an opposite number (positive to negative, or negative to positive)
|
|
58
|
+
// So it can be used to corrent for the offset and get the actual date value we need
|
|
59
|
+
const offset = timezoneHoursOffset * -1;
|
|
60
|
+
|
|
61
|
+
// Calculate the first day of the next month
|
|
62
|
+
const firstDayOfNextMonth = new Date(year, month, dayOfTheMonth, offset);
|
|
63
|
+
|
|
64
|
+
// Calculate the last day of the month
|
|
65
|
+
const lastDay = new Date(year, month + 1, 0, offset);
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
startDate: firstDayOfNextMonth,
|
|
69
|
+
endDate: lastDay,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function daysUntil(targetDate: Date): number {
|
|
74
|
+
const currentDate = new Date();
|
|
75
|
+
const timeDifference = targetDate.getTime() - currentDate.getTime();
|
|
76
|
+
const daysDifference = Math.ceil(timeDifference / (1000 * 3600 * 24));
|
|
77
|
+
return daysDifference;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function firstDayOfCurrentMonth(): Date {
|
|
81
|
+
const currentDate = new Date();
|
|
82
|
+
const year = currentDate.getFullYear();
|
|
83
|
+
const month = currentDate.getMonth();
|
|
84
|
+
const dayOfTheMonth = 1;
|
|
85
|
+
|
|
86
|
+
const timezoneMinutesOffset = new Date(
|
|
87
|
+
year,
|
|
88
|
+
month,
|
|
89
|
+
dayOfTheMonth
|
|
90
|
+
).getTimezoneOffset();
|
|
91
|
+
const timezoneHoursOffset = timezoneMinutesOffset / 60;
|
|
92
|
+
|
|
93
|
+
// We turn the current hours offset into an opposite number (positive to negative, or negative to positive)
|
|
94
|
+
// So it can be used to corrent for the offset and get the actual date value we need
|
|
95
|
+
const offset = timezoneHoursOffset * -1;
|
|
96
|
+
|
|
97
|
+
const resultDate = new Date(year, month, dayOfTheMonth, offset);
|
|
98
|
+
|
|
99
|
+
return resultDate;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function firstDayOfNextMonth(): Date {
|
|
103
|
+
const currentDate = new Date();
|
|
104
|
+
const year = currentDate.getFullYear();
|
|
105
|
+
const month = currentDate.getMonth() + 1;
|
|
106
|
+
const dayOfTheMonth = 1;
|
|
107
|
+
|
|
108
|
+
const timezoneMinutesOffset = new Date(
|
|
109
|
+
year,
|
|
110
|
+
month,
|
|
111
|
+
dayOfTheMonth
|
|
112
|
+
).getTimezoneOffset();
|
|
113
|
+
const timezoneHoursOffset = timezoneMinutesOffset / 60;
|
|
114
|
+
|
|
115
|
+
// We turn the current hours offset into an opposite number (positive to negative, or negative to positive)
|
|
116
|
+
// So it can be used to corrent for the offset and get the actual date value we need
|
|
117
|
+
const offset = timezoneHoursOffset * -1;
|
|
118
|
+
|
|
119
|
+
// Calculate the first day of the next month
|
|
120
|
+
const firstDayOfNextMonth = new Date(year, month, dayOfTheMonth, offset);
|
|
121
|
+
|
|
122
|
+
return firstDayOfNextMonth;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function formatDateShort(date: Date): string {
|
|
126
|
+
const months = [
|
|
127
|
+
'Jan',
|
|
128
|
+
'Feb',
|
|
129
|
+
'Mar',
|
|
130
|
+
'Apr',
|
|
131
|
+
'May',
|
|
132
|
+
'Jun',
|
|
133
|
+
'Jul',
|
|
134
|
+
'Aug',
|
|
135
|
+
'Sep',
|
|
136
|
+
'Oct',
|
|
137
|
+
'Nov',
|
|
138
|
+
'Dec',
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
const month = months[date.getMonth()];
|
|
142
|
+
const day = date.getDate();
|
|
143
|
+
|
|
144
|
+
return `${month} ${day}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function calculatePercentageDifference(
|
|
148
|
+
num1: number,
|
|
149
|
+
num2: number
|
|
150
|
+
): number | null {
|
|
151
|
+
if (num1 === 0) {
|
|
152
|
+
// Avoid division by zero if num1 is 0.
|
|
153
|
+
if (num2 === 0) {
|
|
154
|
+
return 0; // Both numbers are the same.
|
|
155
|
+
} else {
|
|
156
|
+
return 100; // Treat as a 100% increase from 0 to num2.
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const difference = num2 - num1;
|
|
161
|
+
const percentageDifference = (difference / num1) * 100;
|
|
162
|
+
|
|
163
|
+
return percentageDifference;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Returns a date in "YYYY-MM-DD" format
|
|
167
|
+
export function formatDate(date: Date): string {
|
|
168
|
+
return date.toISOString().split('T')[0];
|
|
169
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// Download file from url
|
|
2
|
+
export async function download(url: string, filename?: string) {
|
|
3
|
+
const response = await fetch(url); // CORS is now handled by S3!
|
|
4
|
+
const blob = await response.blob();
|
|
5
|
+
const blobUrl = window.URL.createObjectURL(blob);
|
|
6
|
+
|
|
7
|
+
const a = document.createElement('a');
|
|
8
|
+
a.href = blobUrl;
|
|
9
|
+
|
|
10
|
+
// 1. Use the provided filename, OR
|
|
11
|
+
// 2. Parse the URL, grab the path (ignoring query params), and get the last piece
|
|
12
|
+
const derivedFilename = filename || new URL(url).pathname.split('/').pop();
|
|
13
|
+
|
|
14
|
+
// Only set the download attribute if we successfully resolved a name
|
|
15
|
+
if (derivedFilename) {
|
|
16
|
+
a.download = derivedFilename;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
document.body.appendChild(a);
|
|
20
|
+
a.click();
|
|
21
|
+
document.body.removeChild(a);
|
|
22
|
+
window.URL.revokeObjectURL(blobUrl);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Equivalent to lodash's or underscore's isEmpty, but with better typing and without the dependency
|
|
26
|
+
export function isEmpty(value: any): boolean {
|
|
27
|
+
if (value === null || value === undefined) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (typeof value === 'string' || Array.isArray(value)) {
|
|
32
|
+
return value.length === 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof value === 'object') {
|
|
36
|
+
return Object.keys(value).length === 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Equivalent to lodash's or underscore's isEqual, but with better typing and without the dependency
|
|
43
|
+
export function isEqual(value1: any, value2: any): boolean {
|
|
44
|
+
if (value1 === value2) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (typeof value1 !== typeof value2) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (typeof value1 === 'object' && typeof value2 === 'object') {
|
|
53
|
+
const keys1 = Object.keys(value1);
|
|
54
|
+
const keys2 = Object.keys(value2);
|
|
55
|
+
|
|
56
|
+
if (keys1.length !== keys2.length) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const key of keys1) {
|
|
61
|
+
if (!isEqual(value1[key], value2[key])) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function debounce(func: Function, duration: number, immediate = false) {
|
|
73
|
+
let timeout: any;
|
|
74
|
+
|
|
75
|
+
if (immediate) {
|
|
76
|
+
return function (...args: any[]) {
|
|
77
|
+
const callNow = !timeout;
|
|
78
|
+
clearTimeout(timeout);
|
|
79
|
+
timeout = setTimeout(() => {
|
|
80
|
+
timeout = null;
|
|
81
|
+
}, duration);
|
|
82
|
+
if (callNow) func.apply(this, args);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return function (...args: any[]) {
|
|
87
|
+
const effect = () => {
|
|
88
|
+
timeout = null;
|
|
89
|
+
return func.apply(this, args);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
clearTimeout(timeout);
|
|
93
|
+
timeout = setTimeout(effect, duration);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// random uuid
|
|
98
|
+
export function makeId() {
|
|
99
|
+
let text = '';
|
|
100
|
+
const possible =
|
|
101
|
+
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < 5; i++) {
|
|
104
|
+
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return text;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Returns true if the user is on macOS or an iOS device.
|
|
112
|
+
*/
|
|
113
|
+
export function isMacPlatform(): boolean {
|
|
114
|
+
// 1. New Client Hints API (Chrome, Edge…)
|
|
115
|
+
const uaData = (navigator as any).userAgentData;
|
|
116
|
+
if (uaData && typeof uaData.platform === 'string') {
|
|
117
|
+
const sanitizedPlatform = uaData.platform.toLowerCase();
|
|
118
|
+
return sanitizedPlatform.startsWith('mac') || sanitizedPlatform.startsWith('iphone') || sanitizedPlatform.startsWith('ipad');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// 2. Classic navigator.platform (deprecated, but still widely supported)
|
|
122
|
+
const plat = navigator.platform || '';
|
|
123
|
+
const sanitizedPlat = plat.toLowerCase();
|
|
124
|
+
if (/^mac/i.test(plat) || /^ip(hone|od|ad)/i.test(plat)) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 3. Fallback to sniffing navigator.userAgent
|
|
129
|
+
const ua = navigator.userAgent || '';
|
|
130
|
+
const sanitizedUa = ua.toLowerCase();
|
|
131
|
+
return /\bmac\b/.test(ua) || /\b(iphone|ipad|ipod)\b/.test(sanitizedUa);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Extracts the simple Lambda function name from a full ARN or an API Gateway Integration URI.
|
|
136
|
+
*/
|
|
137
|
+
export function getFunctionNameFromArn(arnOrName: string): string {
|
|
138
|
+
if (!arnOrName) return '';
|
|
139
|
+
if (arnOrName.startsWith('arn:aws:lambda:')) {
|
|
140
|
+
return arnOrName.split(':').pop() || '';
|
|
141
|
+
}
|
|
142
|
+
if (arnOrName.includes('/functions/arn:aws:lambda:')) {
|
|
143
|
+
const match = arnOrName.match(/\/functions\/(arn:aws:lambda:[^/]+)/);
|
|
144
|
+
if (match) {
|
|
145
|
+
return match[1].split(':').pop() || '';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (arnOrName.includes('/functions/')) {
|
|
149
|
+
const parts = arnOrName.split('/functions/');
|
|
150
|
+
const funcPart = parts[1].split('/')[0];
|
|
151
|
+
if (funcPart.startsWith('arn:aws:')) {
|
|
152
|
+
return funcPart.split(':').pop() || '';
|
|
153
|
+
}
|
|
154
|
+
return funcPart;
|
|
155
|
+
}
|
|
156
|
+
return arnOrName;
|
|
157
|
+
}
|
|
158
|
+
|