@lenne.tech/ng-base 18.1.2 → 22.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +55 -0
- package/LICENSE +2 -2
- package/README.md +237 -44
- package/fesm2022/lenne.tech-ng-base-ai.mjs +767 -0
- package/fesm2022/lenne.tech-ng-base-ai.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-auth-provide.mjs +939 -0
- package/fesm2022/lenne.tech-ng-base-auth-provide.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-auth.mjs +454 -0
- package/fesm2022/lenne.tech-ng-base-auth.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs +825 -0
- package/fesm2022/lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs +351 -0
- package/fesm2022/lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-capture-engine-TkjMDdqj.mjs +1046 -0
- package/fesm2022/lenne.tech-ng-base-bug-capture-engine-TkjMDdqj.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-constants-Brm2KGHi.mjs +4 -0
- package/fesm2022/lenne.tech-ng-base-bug-constants-Brm2KGHi.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-messages-BsP4fRQU.mjs +179 -0
- package/fesm2022/lenne.tech-ng-base-bug-messages-BsP4fRQU.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-provide.mjs +26 -0
- package/fesm2022/lenne.tech-ng-base-bug-provide.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-report-Tzlhe1Cm.mjs +62 -0
- package/fesm2022/lenne.tech-ng-base-bug-report-Tzlhe1Cm.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug-server.mjs +818 -0
- package/fesm2022/lenne.tech-ng-base-bug-server.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-bug.mjs +196 -0
- package/fesm2022/lenne.tech-ng-base-bug.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-files-ui.mjs +445 -0
- package/fesm2022/lenne.tech-ng-base-files-ui.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-files.mjs +474 -0
- package/fesm2022/lenne.tech-ng-base-files.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-server.mjs +408 -0
- package/fesm2022/lenne.tech-ng-base-server.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-share.mjs +76 -0
- package/fesm2022/lenne.tech-ng-base-share.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-sync.mjs +1096 -0
- package/fesm2022/lenne.tech-ng-base-sync.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-testing.mjs +171 -0
- package/fesm2022/lenne.tech-ng-base-testing.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-ui-provide.mjs +124 -0
- package/fesm2022/lenne.tech-ng-base-ui-provide.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-ui.mjs +34611 -0
- package/fesm2022/lenne.tech-ng-base-ui.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base-utils.mjs +287 -0
- package/fesm2022/lenne.tech-ng-base-utils.mjs.map +1 -0
- package/fesm2022/lenne.tech-ng-base.mjs +615 -902
- package/fesm2022/lenne.tech-ng-base.mjs.map +1 -1
- package/i18n/auth/de.json +17 -0
- package/i18n/auth/en.json +17 -0
- package/i18n/bug/de.json +85 -0
- package/i18n/bug/en.json +85 -0
- package/i18n/de.json +8 -0
- package/i18n/en.json +8 -0
- package/i18n/files/de.json +27 -0
- package/i18n/files/en.json +27 -0
- package/i18n/share/de.json +8 -0
- package/i18n/share/en.json +8 -0
- package/i18n/ui/de.json +38 -0
- package/i18n/ui/en.json +38 -0
- package/package.json +127 -58
- package/types/lenne.tech-ng-base-ai.d.ts +830 -0
- package/types/lenne.tech-ng-base-auth-provide.d.ts +603 -0
- package/types/lenne.tech-ng-base-auth.d.ts +320 -0
- package/types/lenne.tech-ng-base-bug-provide.d.ts +272 -0
- package/types/lenne.tech-ng-base-bug-rolldown-runtime-C25Le4-w.d.ts +1 -0
- package/types/lenne.tech-ng-base-bug-server.d.ts +316 -0
- package/types/lenne.tech-ng-base-bug.d.ts +202 -0
- package/types/lenne.tech-ng-base-files-ui.d.ts +116 -0
- package/types/lenne.tech-ng-base-files.d.ts +288 -0
- package/types/lenne.tech-ng-base-server.d.ts +262 -0
- package/types/lenne.tech-ng-base-share.d.ts +21 -0
- package/types/lenne.tech-ng-base-sync.d.ts +380 -0
- package/types/lenne.tech-ng-base-testing.d.ts +327 -0
- package/types/lenne.tech-ng-base-ui-provide.d.ts +65 -0
- package/types/lenne.tech-ng-base-ui.d.ts +7732 -0
- package/types/lenne.tech-ng-base-utils.d.ts +150 -0
- package/types/lenne.tech-ng-base.d.ts +503 -0
- package/ui/styles/components/accordion.css +3 -0
- package/ui/styles/components/alert-dialog.css +4 -0
- package/ui/styles/components/alert.css +3 -0
- package/ui/styles/components/aspect-ratio.css +3 -0
- package/ui/styles/components/attachment.css +4 -0
- package/ui/styles/components/autocomplete.css +4 -0
- package/ui/styles/components/avatar.css +3 -0
- package/ui/styles/components/badge.css +3 -0
- package/ui/styles/components/breadcrumb.css +3 -0
- package/ui/styles/components/bubble.css +3 -0
- package/ui/styles/components/button-group.css +3 -0
- package/ui/styles/components/button.css +3 -0
- package/ui/styles/components/calendar.css +5 -0
- package/ui/styles/components/card.css +3 -0
- package/ui/styles/components/checkbox.css +3 -0
- package/ui/styles/components/code-block.css +4 -0
- package/ui/styles/components/collapsible.css +3 -0
- package/ui/styles/components/combobox.css +5 -0
- package/ui/styles/components/command.css +5 -0
- package/ui/styles/components/context-menu.css +3 -0
- package/ui/styles/components/date-picker.css +7 -0
- package/ui/styles/components/dialog.css +4 -0
- package/ui/styles/components/drawer.css +3 -0
- package/ui/styles/components/dropdown-menu.css +3 -0
- package/ui/styles/components/empty.css +3 -0
- package/ui/styles/components/field.css +5 -0
- package/ui/styles/components/hover-card.css +3 -0
- package/ui/styles/components/index.css +91 -0
- package/ui/styles/components/input-group.css +6 -0
- package/ui/styles/components/input-otp.css +3 -0
- package/ui/styles/components/input.css +3 -0
- package/ui/styles/components/item.css +4 -0
- package/ui/styles/components/kbd.css +3 -0
- package/ui/styles/components/label.css +3 -0
- package/ui/styles/components/lt-breadcrumb.css +3 -0
- package/ui/styles/components/lt-bug-attachments.css +5 -0
- package/ui/styles/components/lt-bug-journey-timeline.css +5 -0
- package/ui/styles/components/lt-bug-report-dialog.css +15 -0
- package/ui/styles/components/lt-bug-report.css +4 -0
- package/ui/styles/components/lt-color-mode-button.css +3 -0
- package/ui/styles/components/lt-confirm-dialog.css +4 -0
- package/ui/styles/components/lt-data-view.css +5 -0
- package/ui/styles/components/lt-date-picker.css +3 -0
- package/ui/styles/components/lt-date-time-picker.css +5 -0
- package/ui/styles/components/lt-enter-action.css +2 -0
- package/ui/styles/components/lt-footer.css +3 -0
- package/ui/styles/components/lt-form-field.css +4 -0
- package/ui/styles/components/lt-header.css +5 -0
- package/ui/styles/components/lt-imports.css +21 -0
- package/ui/styles/components/lt-navigation-menu.css +3 -0
- package/ui/styles/components/lt-notice.css +3 -0
- package/ui/styles/components/lt-overlay-defaults.css +3 -0
- package/ui/styles/components/lt-page-card.css +3 -0
- package/ui/styles/components/lt-page-title.css +2 -0
- package/ui/styles/components/lt-password-input.css +4 -0
- package/ui/styles/components/lt-readonly-field.css +2 -0
- package/ui/styles/components/lt-select.css +4 -0
- package/ui/styles/components/lt-text-field.css +5 -0
- package/ui/styles/components/lt-toolbar-item.css +2 -0
- package/ui/styles/components/lt-toolbar.css +3 -0
- package/ui/styles/components/lt-tus-upload-component.css +5 -0
- package/ui/styles/components/marker.css +3 -0
- package/ui/styles/components/menubar.css +3 -0
- package/ui/styles/components/message-scroller.css +4 -0
- package/ui/styles/components/message.css +3 -0
- package/ui/styles/components/native-select.css +3 -0
- package/ui/styles/components/navigation-menu.css +3 -0
- package/ui/styles/components/pagination.css +4 -0
- package/ui/styles/components/popover.css +3 -0
- package/ui/styles/components/progress.css +3 -0
- package/ui/styles/components/questionnaire.css +4 -0
- package/ui/styles/components/radio-group.css +3 -0
- package/ui/styles/components/resizable.css +3 -0
- package/ui/styles/components/select.css +3 -0
- package/ui/styles/components/separator.css +3 -0
- package/ui/styles/components/sheet.css +4 -0
- package/ui/styles/components/sidebar.css +9 -0
- package/ui/styles/components/skeleton.css +3 -0
- package/ui/styles/components/slider.css +3 -0
- package/ui/styles/components/sonner.css +4 -0
- package/ui/styles/components/spinner.css +3 -0
- package/ui/styles/components/switch.css +3 -0
- package/ui/styles/components/table.css +3 -0
- package/ui/styles/components/tabs.css +4 -0
- package/ui/styles/components/textarea.css +3 -0
- package/ui/styles/components/toggle-group.css +4 -0
- package/ui/styles/components/toggle.css +3 -0
- package/ui/styles/components/tooltip.css +3 -0
- package/ui/styles/components/trace.css +3 -0
- package/ui/styles/components/typography.css +3 -0
- package/ui/styles/components/utils.css +2 -0
- package/ui/styles/core.css +137 -0
- package/ui/styles/palette.css +94 -0
- package/ui/styles/theme.css +14 -0
- package/ui/styles/transitions.css +131 -0
- package/base-cms/index.d.ts +0 -5
- package/base-cms/interfaces/base-cms-module-config.interface.d.ts +0 -16
- package/base-cms/pages/base-cms/base-cms.component.d.ts +0 -92
- package/base-cms/public-api.d.ts +0 -5
- package/base-components/components/breadcrumb/breadcrumb.component.d.ts +0 -10
- package/base-components/components/change-password/change-password.component.d.ts +0 -24
- package/base-components/components/checkbox/checkbox.component.d.ts +0 -12
- package/base-components/components/delete-account/delete-account.component.d.ts +0 -15
- package/base-components/components/fab-button/fab-button.component.d.ts +0 -16
- package/base-components/components/input/input.component.d.ts +0 -27
- package/base-components/components/loading-bar/loading-bar.component.d.ts +0 -17
- package/base-components/components/model-form/model-form-base.component.d.ts +0 -64
- package/base-components/components/model-form/model-form.component.d.ts +0 -139
- package/base-components/components/model-form-sub/model-form-sub.component.d.ts +0 -56
- package/base-components/components/model-table/model-table.component.d.ts +0 -115
- package/base-components/components/refence-input/refence-input.component.d.ts +0 -84
- package/base-components/components/select/select.component.d.ts +0 -25
- package/base-components/components/skeleton-loader/skeleton-loader.component.d.ts +0 -10
- package/base-components/components/tags/tags.component.d.ts +0 -60
- package/base-components/components/textarea/textarea.component.d.ts +0 -21
- package/base-components/components/toast/toast.component.d.ts +0 -19
- package/base-components/components/upload-image/upload-image.component.d.ts +0 -111
- package/base-components/directives/back-button.directive.d.ts +0 -9
- package/base-components/directives/context-menu.directive.d.ts +0 -22
- package/base-components/directives/sort.directive.d.ts +0 -15
- package/base-components/directives/stop-propagation.directive.d.ts +0 -10
- package/base-components/index.d.ts +0 -5
- package/base-components/pipes/ellipses.pipe.d.ts +0 -10
- package/base-components/public-api.d.ts +0 -26
- package/base-prototype/index.d.ts +0 -5
- package/base-prototype/pages/email-verification/email-verification.component.d.ts +0 -18
- package/base-prototype/pages/forgot-password/forgot-password.component.d.ts +0 -26
- package/base-prototype/pages/login/login.component.d.ts +0 -33
- package/base-prototype/pages/profile/profile.component.d.ts +0 -24
- package/base-prototype/pages/register/register.component.d.ts +0 -31
- package/base-prototype/pages/set-password/set-password.component.d.ts +0 -29
- package/base-prototype/public-api.d.ts +0 -9
- package/esm2022/base-cms/interfaces/base-cms-module-config.interface.mjs +0 -3
- package/esm2022/base-cms/lenne.tech-ng-base-base-cms.mjs +0 -5
- package/esm2022/base-cms/pages/base-cms/base-cms.component.mjs +0 -238
- package/esm2022/base-cms/public-api.mjs +0 -8
- package/esm2022/base-components/components/breadcrumb/breadcrumb.component.mjs +0 -23
- package/esm2022/base-components/components/change-password/change-password.component.mjs +0 -97
- package/esm2022/base-components/components/checkbox/checkbox.component.mjs +0 -38
- package/esm2022/base-components/components/delete-account/delete-account.component.mjs +0 -59
- package/esm2022/base-components/components/fab-button/fab-button.component.mjs +0 -24
- package/esm2022/base-components/components/input/input.component.mjs +0 -75
- package/esm2022/base-components/components/loading-bar/loading-bar.component.mjs +0 -37
- package/esm2022/base-components/components/model-form/model-form-base.component.mjs +0 -145
- package/esm2022/base-components/components/model-form/model-form.component.mjs +0 -591
- package/esm2022/base-components/components/model-form-sub/model-form-sub.component.mjs +0 -117
- package/esm2022/base-components/components/model-table/model-table.component.mjs +0 -478
- package/esm2022/base-components/components/refence-input/refence-input.component.mjs +0 -278
- package/esm2022/base-components/components/select/select.component.mjs +0 -62
- package/esm2022/base-components/components/skeleton-loader/skeleton-loader.component.mjs +0 -33
- package/esm2022/base-components/components/tags/tags.component.mjs +0 -227
- package/esm2022/base-components/components/textarea/textarea.component.mjs +0 -79
- package/esm2022/base-components/components/toast/toast.component.mjs +0 -69
- package/esm2022/base-components/components/upload-image/upload-image.component.mjs +0 -346
- package/esm2022/base-components/directives/back-button.directive.mjs +0 -25
- package/esm2022/base-components/directives/context-menu.directive.mjs +0 -112
- package/esm2022/base-components/directives/sort.directive.mjs +0 -61
- package/esm2022/base-components/directives/stop-propagation.directive.mjs +0 -24
- package/esm2022/base-components/lenne.tech-ng-base-base-components.mjs +0 -5
- package/esm2022/base-components/pipes/ellipses.pipe.mjs +0 -23
- package/esm2022/base-components/public-api.mjs +0 -30
- package/esm2022/base-prototype/lenne.tech-ng-base-base-prototype.mjs +0 -5
- package/esm2022/base-prototype/pages/email-verification/email-verification.component.mjs +0 -60
- package/esm2022/base-prototype/pages/forgot-password/forgot-password.component.mjs +0 -78
- package/esm2022/base-prototype/pages/login/login.component.mjs +0 -114
- package/esm2022/base-prototype/pages/profile/profile.component.mjs +0 -87
- package/esm2022/base-prototype/pages/register/register.component.mjs +0 -100
- package/esm2022/base-prototype/pages/set-password/set-password.component.mjs +0 -91
- package/esm2022/base-prototype/public-api.mjs +0 -11
- package/esm2022/lenne.tech-ng-base.mjs +0 -5
- package/esm2022/public-api.mjs +0 -25
- package/esm2022/shared/classes/auth.class.mjs +0 -14
- package/esm2022/shared/classes/basic-user.class.mjs +0 -49
- package/esm2022/shared/classes/graphql-enum.class.mjs +0 -10
- package/esm2022/shared/classes/graphql-meta.class.mjs +0 -345
- package/esm2022/shared/classes/graphql-type.class.mjs +0 -14
- package/esm2022/shared/classes/helper.class.mjs +0 -307
- package/esm2022/shared/classes/sort.class.mjs +0 -38
- package/esm2022/shared/classes/standard.class.mjs +0 -40
- package/esm2022/shared/decorators/debounce.decorator.mjs +0 -12
- package/esm2022/shared/enums/comparison-operator.enum.mjs +0 -16
- package/esm2022/shared/enums/graphql-request-type.enum.mjs +0 -10
- package/esm2022/shared/enums/logical-operator.enum.mjs +0 -10
- package/esm2022/shared/enums/skeleton-type.enum.mjs +0 -7
- package/esm2022/shared/enums/sort-order.enum.mjs +0 -6
- package/esm2022/shared/enums/toast-type.enum.mjs +0 -8
- package/esm2022/shared/functions/ws-client.function.mjs +0 -39
- package/esm2022/shared/interfaces/base-module-config.interface.mjs +0 -3
- package/esm2022/shared/interfaces/breadcrumb.interface.mjs +0 -2
- package/esm2022/shared/interfaces/can-component-deactivate.interface.mjs +0 -2
- package/esm2022/shared/interfaces/cms-enum-values.interface.mjs +0 -2
- package/esm2022/shared/interfaces/cms-field-config.interface.mjs +0 -2
- package/esm2022/shared/interfaces/cms-model-config.interface.mjs +0 -2
- package/esm2022/shared/interfaces/cms-table-field.interface.mjs +0 -2
- package/esm2022/shared/interfaces/combined-filter-input.interface.mjs +0 -2
- package/esm2022/shared/interfaces/compress-options.interface.mjs +0 -2
- package/esm2022/shared/interfaces/cropping-options.interface.mjs +0 -2
- package/esm2022/shared/interfaces/field-object.interface.mjs +0 -2
- package/esm2022/shared/interfaces/field.interface.mjs +0 -2
- package/esm2022/shared/interfaces/filter-input.interface.mjs +0 -2
- package/esm2022/shared/interfaces/find-args.interface.mjs +0 -2
- package/esm2022/shared/interfaces/graphql-crud-type.interface.mjs +0 -2
- package/esm2022/shared/interfaces/graphql-fields-object.interface.mjs +0 -2
- package/esm2022/shared/interfaces/graphql-options.interface.mjs +0 -2
- package/esm2022/shared/interfaces/graphql-plus-options.interfacen.mjs +0 -2
- package/esm2022/shared/interfaces/graphql-type-collection.interface.mjs +0 -2
- package/esm2022/shared/interfaces/login-config.interface.mjs +0 -2
- package/esm2022/shared/interfaces/register-config.interface.mjs +0 -2
- package/esm2022/shared/interfaces/single-filter-input.interface.mjs +0 -2
- package/esm2022/shared/interfaces/sort-input.interface.mjs +0 -2
- package/esm2022/shared/interfaces/time-components.interface.mjs +0 -2
- package/esm2022/shared/interfaces/toast.interface.mjs +0 -2
- package/esm2022/shared/lenne.tech-ng-base-shared.mjs +0 -5
- package/esm2022/shared/public-api.mjs +0 -74
- package/esm2022/shared/services/auth.service.mjs +0 -152
- package/esm2022/shared/services/breadcrumb.service.mjs +0 -69
- package/esm2022/shared/services/cms.service.mjs +0 -82
- package/esm2022/shared/services/file.service.mjs +0 -86
- package/esm2022/shared/services/forms.service.mjs +0 -61
- package/esm2022/shared/services/graphql-meta.service.mjs +0 -132
- package/esm2022/shared/services/graphql-plus.service.mjs +0 -93
- package/esm2022/shared/services/graphql.service.mjs +0 -510
- package/esm2022/shared/services/image.service.mjs +0 -102
- package/esm2022/shared/services/loader.service.mjs +0 -78
- package/esm2022/shared/services/navigation.service.mjs +0 -38
- package/esm2022/shared/services/scroll.service.mjs +0 -207
- package/esm2022/shared/services/storage.service.mjs +0 -149
- package/esm2022/shared/services/toast.service.mjs +0 -39
- package/esm2022/shared/services/user.service.mjs +0 -160
- package/esm2022/shared/services/ws.service.mjs +0 -17
- package/esm2022/shared/types/graphql-fields.type.mjs +0 -2
- package/esm2022/shared/types/storage.type.mjs +0 -2
- package/esm2022/shared/validators/email.validator.mjs +0 -7
- package/esm2022/shared/validators/match.validator.mjs +0 -22
- package/esm2022/shared/validators/secure-password.validator.mjs +0 -7
- package/esm2022/src/base.module.mjs +0 -64
- package/esm2022/src/decorators/auto-unsubscribe.decorator.mjs +0 -23
- package/esm2022/src/directives/lazy-load.directive.mjs +0 -48
- package/esm2022/src/directives/match-height.directive.mjs +0 -92
- package/esm2022/src/directives/resizable.directive.mjs +0 -92
- package/esm2022/src/factories/apollo-options.factory.mjs +0 -108
- package/esm2022/src/functions/extract-files.function.mjs +0 -87
- package/esm2022/src/guards/auth.guard.mjs +0 -91
- package/esm2022/src/guards/can-deactivate.guard.mjs +0 -16
- package/esm2022/src/guards/logout.guard.mjs +0 -34
- package/esm2022/src/pipes/date-ago.pipe.mjs +0 -64
- package/esm2022/src/pipes/safe-html.pipe.mjs +0 -22
- package/esm2022/src/services/fullscreen.service.mjs +0 -51
- package/esm2022/src/services/seo.service.mjs +0 -109
- package/esm2022/src/services/theme.service.mjs +0 -44
- package/fesm2022/lenne.tech-ng-base-base-cms.mjs +0 -251
- package/fesm2022/lenne.tech-ng-base-base-cms.mjs.map +0 -1
- package/fesm2022/lenne.tech-ng-base-base-components.mjs +0 -2917
- package/fesm2022/lenne.tech-ng-base-base-components.mjs.map +0 -1
- package/fesm2022/lenne.tech-ng-base-base-prototype.mjs +0 -505
- package/fesm2022/lenne.tech-ng-base-base-prototype.mjs.map +0 -1
- package/fesm2022/lenne.tech-ng-base-shared.mjs +0 -2863
- package/fesm2022/lenne.tech-ng-base-shared.mjs.map +0 -1
- package/index.d.ts +0 -5
- package/public-api.d.ts +0 -17
- package/shared/classes/auth.class.d.ts +0 -10
- package/shared/classes/basic-user.class.d.ts +0 -27
- package/shared/classes/graphql-enum.class.d.ts +0 -7
- package/shared/classes/graphql-meta.class.d.ts +0 -66
- package/shared/classes/graphql-type.class.d.ts +0 -13
- package/shared/classes/helper.class.d.ts +0 -99
- package/shared/classes/sort.class.d.ts +0 -12
- package/shared/classes/standard.class.d.ts +0 -32
- package/shared/decorators/debounce.decorator.d.ts +0 -1
- package/shared/enums/comparison-operator.enum.d.ts +0 -14
- package/shared/enums/graphql-request-type.enum.d.ts +0 -8
- package/shared/enums/logical-operator.enum.d.ts +0 -8
- package/shared/enums/skeleton-type.enum.d.ts +0 -5
- package/shared/enums/sort-order.enum.d.ts +0 -4
- package/shared/enums/toast-type.enum.d.ts +0 -6
- package/shared/functions/ws-client.function.d.ts +0 -5
- package/shared/index.d.ts +0 -5
- package/shared/interfaces/base-module-config.interface.d.ts +0 -61
- package/shared/interfaces/breadcrumb.interface.d.ts +0 -4
- package/shared/interfaces/can-component-deactivate.interface.d.ts +0 -7
- package/shared/interfaces/cms-enum-values.interface.d.ts +0 -4
- package/shared/interfaces/cms-field-config.interface.d.ts +0 -45
- package/shared/interfaces/cms-model-config.interface.d.ts +0 -14
- package/shared/interfaces/cms-table-field.interface.d.ts +0 -10
- package/shared/interfaces/combined-filter-input.interface.d.ts +0 -9
- package/shared/interfaces/compress-options.interface.d.ts +0 -15
- package/shared/interfaces/cropping-options.interface.d.ts +0 -12
- package/shared/interfaces/field-object.interface.d.ts +0 -6
- package/shared/interfaces/field.interface.d.ts +0 -5
- package/shared/interfaces/filter-input.interface.d.ts +0 -9
- package/shared/interfaces/find-args.interface.d.ts +0 -11
- package/shared/interfaces/graphql-crud-type.interface.d.ts +0 -7
- package/shared/interfaces/graphql-fields-object.interface.d.ts +0 -6
- package/shared/interfaces/graphql-options.interface.d.ts +0 -11
- package/shared/interfaces/graphql-plus-options.interfacen.d.ts +0 -9
- package/shared/interfaces/graphql-type-collection.interface.d.ts +0 -7
- package/shared/interfaces/login-config.interface.d.ts +0 -6
- package/shared/interfaces/register-config.interface.d.ts +0 -4
- package/shared/interfaces/single-filter-input.interface.d.ts +0 -11
- package/shared/interfaces/sort-input.interface.d.ts +0 -5
- package/shared/interfaces/time-components.interface.d.ts +0 -6
- package/shared/interfaces/toast.interface.d.ts +0 -8
- package/shared/public-api.d.ts +0 -65
- package/shared/services/auth.service.d.ts +0 -44
- package/shared/services/breadcrumb.service.d.ts +0 -32
- package/shared/services/cms.service.d.ts +0 -27
- package/shared/services/file.service.d.ts +0 -22
- package/shared/services/forms.service.d.ts +0 -14
- package/shared/services/graphql-meta.service.d.ts +0 -45
- package/shared/services/graphql-plus.service.d.ts +0 -27
- package/shared/services/graphql.service.d.ts +0 -71
- package/shared/services/image.service.d.ts +0 -27
- package/shared/services/loader.service.d.ts +0 -32
- package/shared/services/navigation.service.d.ts +0 -15
- package/shared/services/scroll.service.d.ts +0 -85
- package/shared/services/storage.service.d.ts +0 -49
- package/shared/services/toast.service.d.ts +0 -15
- package/shared/services/user.service.d.ts +0 -77
- package/shared/services/ws.service.d.ts +0 -8
- package/shared/types/graphql-fields.type.d.ts +0 -5
- package/shared/types/storage.type.d.ts +0 -4
- package/shared/validators/email.validator.d.ts +0 -1
- package/shared/validators/match.validator.d.ts +0 -4
- package/shared/validators/secure-password.validator.d.ts +0 -1
- package/src/base.module.d.ts +0 -16
- package/src/decorators/auto-unsubscribe.decorator.d.ts +0 -5
- package/src/directives/lazy-load.directive.d.ts +0 -17
- package/src/directives/match-height.directive.d.ts +0 -20
- package/src/directives/resizable.directive.d.ts +0 -26
- package/src/factories/apollo-options.factory.d.ts +0 -16
- package/src/functions/extract-files.function.d.ts +0 -15
- package/src/guards/auth.guard.d.ts +0 -27
- package/src/guards/can-deactivate.guard.d.ts +0 -10
- package/src/guards/logout.guard.d.ts +0 -16
- package/src/pipes/date-ago.pipe.d.ts +0 -34
- package/src/pipes/safe-html.pipe.d.ts +0 -13
- package/src/services/fullscreen.service.d.ts +0 -18
- package/src/services/seo.service.d.ts +0 -50
- package/src/services/theme.service.d.ts +0 -17
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lenne.tech-ng-base-bug-server.mjs","names":[],"sources":["../../../projects/ng-base/bug/server/src/lib/linear-client.ts","../../../projects/ng-base/bug/server/src/lib/linear-format.ts","../../../projects/ng-base/bug/server/src/lib/redact.ts","../../../projects/ng-base/bug/server/src/lib/validate.ts","../../../projects/ng-base/bug/server/src/lib/bug-report-handler.ts"],"sourcesContent":["/// <reference types=\"node\" />\n\n/** Linear GraphQL endpoint. */\nexport const LT_LINEAR_API_URL = 'https://api.linear.app/graphql';\n\n/** Label name and color per report type (same mapping as `@lenne.tech/bug.lt`). */\nexport const LT_BUG_LABELS: Readonly<Record<string, { color: string; name: string }>> = Object.freeze({\n bug: { color: '#f87171', name: 'bug' },\n enhancement: { color: '#34d399', name: 'enhancement' },\n feature: { color: '#60a5fa', name: 'feature' },\n other: { color: '#a78bfa', name: 'other' },\n});\n\n/** Error of a Linear request. The message may contain Linear's answer: log it server-side only, never send it out. */\nexport class LtLinearError extends Error {\n constructor(\n message: string,\n readonly status?: number,\n ) {\n super(message);\n this.name = 'LtLinearError';\n }\n}\n\n/** Options of {@link LtLinearClient}. */\nexport interface LtLinearClientOptions {\n /** Linear personal API key (sent as `Authorization`). */\n apiKey: string;\n /** GraphQL endpoint (default {@link LT_LINEAR_API_URL}). */\n apiUrl?: string;\n /** `fetch` implementation (default the global `fetch`). */\n fetch?: typeof fetch;\n /** Timeout per request in ms (default 15000). */\n timeoutMs?: number;\n}\n\n/** Issue created by {@link LtLinearClient.createIssue}. */\nexport interface LtLinearIssue {\n id: string;\n identifier?: string;\n url?: string;\n}\n\ninterface Page<T> {\n nodes: T[];\n pageInfo?: { endCursor?: string | null; hasNextPage?: boolean };\n}\n\nconst MAX_PAGES = 10;\n\n/**\n * Minimal Linear GraphQL client for bug reports (the port of `LinearApiClient` of `@lenne.tech/bug.lt`): team and project\n * lookup by name (paginated, case-insensitive), label lookup/creation, issue creation and the two-step file upload\n * (signed URL from `fileUpload`, `PUT` to storage, then `attachmentCreate`).\n */\nexport class LtLinearClient {\n private readonly apiUrl: string;\n private readonly fetchFn: typeof fetch;\n private readonly timeoutMs: number;\n\n constructor(private readonly options: LtLinearClientOptions) {\n this.apiUrl = options.apiUrl ?? LT_LINEAR_API_URL;\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.timeoutMs = options.timeoutMs ?? 15_000;\n }\n\n /** Run a GraphQL request; throws {@link LtLinearError} on HTTP and GraphQL errors. */\n async request<T>(query: string, variables: Record<string, unknown> = {}): Promise<T> {\n let response: Response;\n try {\n response = await this.fetchFn(this.apiUrl, {\n body: JSON.stringify({ query, variables }),\n headers: { Authorization: this.options.apiKey, 'Content-Type': 'application/json' },\n method: 'POST',\n signal: AbortSignal.timeout(this.timeoutMs),\n });\n } catch (error) {\n throw new LtLinearError(`Linear API request failed: ${error instanceof Error ? error.message : String(error)}`);\n }\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new LtLinearError(`Linear API request failed: ${response.status} ${response.statusText} - ${text.slice(0, 500)}`, response.status);\n }\n const result = (await response.json().catch(() => null)) as { data?: T; errors?: unknown[] } | null;\n if (!result) {\n throw new LtLinearError('Linear API answered with invalid JSON');\n }\n if (result.errors?.length) {\n throw new LtLinearError(`Linear GraphQL error: ${JSON.stringify(result.errors).slice(0, 1000)}`);\n }\n return result.data as T;\n }\n\n /** All teams (paginated). */\n async getTeams(): Promise<{ id: string; key: string; name: string }[]> {\n return this.collect<{ id: string; key: string; name: string }>(\n 'query Teams($after: String) { teams(first: 250, after: $after) { nodes { id name key } pageInfo { hasNextPage endCursor } } }',\n (data) => (data as { teams?: Page<{ id: string; key: string; name: string }> }).teams,\n );\n }\n\n /** Team by name or key (case-insensitive), `null` when none matches. */\n async findTeam(nameOrKey: string): Promise<{ id: string; key: string; name: string } | null> {\n const wanted = nameOrKey.trim().toLowerCase();\n return (await this.getTeams()).find((team) => team.name.toLowerCase() === wanted || team.key.toLowerCase() === wanted) ?? null;\n }\n\n /** Project by name (case-insensitive), `null` when none matches. */\n async findProject(name: string): Promise<{ id: string; name: string } | null> {\n const wanted = name.trim().toLowerCase();\n const projects = await this.collect<{ id: string; name: string }>(\n 'query Projects($after: String) { projects(first: 250, after: $after) { nodes { id name } pageInfo { hasNextPage endCursor } } }',\n (data) => (data as { projects?: Page<{ id: string; name: string }> }).projects,\n );\n return projects.find((project) => project.name.toLowerCase() === wanted) ?? null;\n }\n\n /** Id of the label `name` usable in `teamId` (a team label or a workspace label), `null` when there is none. */\n async findLabel(teamId: string, name: string): Promise<string | null> {\n const wanted = name.toLowerCase();\n const labels = await this.collect<{ id: string; name: string; team?: { id: string } | null }>(\n 'query Labels($after: String) { issueLabels(first: 250, after: $after) { nodes { id name team { id } } pageInfo { hasNextPage endCursor } } }',\n (data) => (data as { issueLabels?: Page<{ id: string; name: string; team?: { id: string } | null }> }).issueLabels,\n );\n const matches = labels.filter((label) => label.name.toLowerCase() === wanted && (!label.team || label.team.id === teamId));\n return (matches.find((label) => label.team?.id === teamId) ?? matches[0])?.id ?? null;\n }\n\n /** Create a team label; returns its id. */\n async createLabel(teamId: string, name: string, color: string): Promise<string | null> {\n const data = await this.request<{ issueLabelCreate?: { issueLabel?: { id: string }; success: boolean } }>(\n 'mutation CreateLabel($input: IssueLabelCreateInput!) { issueLabelCreate(input: $input) { success issueLabel { id name } } }',\n { input: { color, name, teamId } },\n );\n return data.issueLabelCreate?.success ? (data.issueLabelCreate.issueLabel?.id ?? null) : null;\n }\n\n /** Create an issue. */\n async createIssue(input: { description: string; labelIds?: string[]; projectId?: string; teamId: string; title: string }): Promise<LtLinearIssue> {\n const payload: Record<string, unknown> = { description: input.description, teamId: input.teamId, title: input.title };\n if (input.projectId) {\n payload['projectId'] = input.projectId;\n }\n if (input.labelIds?.length) {\n payload['labelIds'] = input.labelIds;\n }\n const data = await this.request<{ issueCreate?: { issue?: LtLinearIssue; success: boolean } }>(\n 'mutation CreateIssue($input: IssueCreateInput!) { issueCreate(input: $input) { success issue { id url identifier title } } }',\n { input: payload },\n );\n if (!data.issueCreate?.success || !data.issueCreate.issue?.id) {\n throw new LtLinearError('Linear did not create the issue');\n }\n return data.issueCreate.issue;\n }\n\n /** Upload a file to Linear's storage; returns the asset URL. */\n async uploadFile(content: Uint8Array, filename: string, contentType: string): Promise<string> {\n if (!content.length) {\n throw new LtLinearError(`File ${filename} has zero size`);\n }\n const data = await this.request<{\n fileUpload?: { success: boolean; uploadFile?: { assetUrl: string; headers?: { key: string; value: string }[]; uploadUrl: string } };\n }>(\n 'mutation FileUpload($contentType: String!, $filename: String!, $size: Int!) { fileUpload(contentType: $contentType, filename: $filename, size: $size) { success uploadFile { uploadUrl assetUrl headers { key value } } } }',\n { contentType, filename, size: content.length },\n );\n const upload = data.fileUpload?.uploadFile;\n if (!data.fileUpload?.success || !upload?.uploadUrl || !upload.assetUrl) {\n throw new LtLinearError('Failed to get upload URL from Linear');\n }\n if (!/^https:\\/\\//i.test(upload.uploadUrl)) {\n throw new LtLinearError('Linear returned a non-HTTPS upload URL');\n }\n const headers = new Headers({ 'Content-Type': contentType });\n for (const { key, value } of upload.headers ?? []) {\n headers.set(key, value);\n }\n let response: Response;\n try {\n // Copy into a plain ArrayBuffer-backed view (`BodyInit` does not take views over a SharedArrayBuffer).\n const body = new Uint8Array(content.byteLength);\n body.set(content);\n response = await this.fetchFn(upload.uploadUrl, { body, headers, method: 'PUT', signal: AbortSignal.timeout(this.timeoutMs * 4) });\n } catch (error) {\n throw new LtLinearError(`Failed to upload file: ${error instanceof Error ? error.message : String(error)}`);\n }\n if (!response.ok) {\n throw new LtLinearError(`Failed to upload file: ${response.status} ${response.statusText}`, response.status);\n }\n return upload.assetUrl;\n }\n\n /** Link an uploaded file to an issue. */\n async createAttachment(issueId: string, url: string, title: string): Promise<string | null> {\n const data = await this.request<{ attachmentCreate?: { attachment?: { id: string }; success: boolean } }>(\n 'mutation CreateAttachment($input: AttachmentCreateInput!) { attachmentCreate(input: $input) { success attachment { id } } }',\n { input: { issueId, title, url } },\n );\n return data.attachmentCreate?.success ? (data.attachmentCreate.attachment?.id ?? null) : null;\n }\n\n private async collect<T>(query: string, pick: (data: unknown) => Page<T> | undefined): Promise<T[]> {\n const items: T[] = [];\n let after: string | null = null;\n for (let page = 0; page < MAX_PAGES; page++) {\n const connection: Page<T> | undefined = pick(await this.request<unknown>(query, { after }));\n items.push(...(connection?.nodes ?? []));\n if (!connection?.pageInfo?.hasNextPage || !connection.pageInfo.endCursor) {\n break;\n }\n after = connection.pageInfo.endCursor;\n }\n return items;\n }\n}\n","import type { LtBugServerReport } from './validate';\n\n/** Options of {@link formatLtBugReportForLinear}. */\nexport interface LtLinearFormatOptions {\n /** Time zone of the journey times (default `Europe/Berlin`; the browser sends UTC timestamps). */\n timeZone?: string;\n}\n\n/** Title and Markdown description of the Linear issue. */\nexport interface LtLinearIssueText {\n description: string;\n title: string;\n}\n\n/**\n * Linear issue text of a report, in the layout of `@lenne.tech/bug.lt` (German headings): bug report, context table,\n * user journey table, technical details (last 10 console logs and network requests, failed requests with the start of\n * their response) and an attachments note. Table cells are escaped, so `|` or line breaks in data keep the tables intact.\n */\nexport function formatLtBugReportForLinear(report: LtBugServerReport, options: LtLinearFormatOptions = {}): LtLinearIssueText {\n const timeZone = options.timeZone ?? 'Europe/Berlin';\n const sections: string[] = [];\n\n const reportParts = [`**Beschreibung:**\\n${report.description}`];\n if (report.expectedBehavior) {\n reportParts.push(`**Erwartetes Verhalten:**\\n${report.expectedBehavior}`);\n }\n if (report.stepsToReproduce) {\n reportParts.push(`**Schritte zur Reproduktion:**\\n${report.stepsToReproduce}`);\n }\n sections.push(`## Bug Report\\n\\n${reportParts.join('\\n\\n')}`);\n\n const info = report.browserInfo;\n if (info) {\n const browser = `${info.browser?.name || 'Unknown'} ${info.browser?.version || ''}`.trim();\n const os = `${info.os?.name || info.platform || 'Unknown'} ${info.os?.version || ''}`.trim();\n sections.push(\n [\n '---',\n '',\n '## Kontext',\n '',\n '| | |',\n '|---|---|',\n `| **URL** | ${cell(info.url ?? '')} |`,\n `| **Browser** | ${cell(browser)} |`,\n `| **OS** | ${cell(os)} |`,\n `| **Viewport** | ${info.viewport ? `${info.viewport.width}x${info.viewport.height}` : ''} |`,\n `| **Zeitpunkt** | ${cell(info.timestamp ?? '')} |`,\n ].join('\\n'),\n );\n }\n\n if (report.userInteractions.length) {\n const rows = report.userInteractions.map(\n (event) => `| ${time(event.timestamp, timeZone)} | ${cell(eventType(event.type))} | ${cell(shorten(event.target, 40))} | ${cell(eventDetails(event))} |`,\n );\n sections.push(\n [\n '---',\n '',\n '## User Journey',\n '',\n `> Letzte ${report.userInteractions.length} Aktionen vor dem Bug`,\n '',\n '| Zeit | Aktion | Element | Details |',\n '|------|--------|---------|---------|',\n ...rows,\n ].join('\\n'),\n );\n }\n\n const technical: string[] = [];\n if (report.consoleLogs.length) {\n const logs = report.consoleLogs.slice(-10);\n const lines = logs.map((log) => `[${log.timestamp}] ${log.level.toUpperCase()}: ${log.message.join(' ')}`.replace(/```/g, '```'));\n technical.push(`### Console Logs (${logs.length})\\n\\n\\`\\`\\`\\n${lines.join('\\n')}\\n\\`\\`\\``);\n }\n if (report.networkRequests.length) {\n const requests = report.networkRequests.slice(-10);\n const failed = requests.filter((request) => request.status >= 400 || request.status === 0);\n let content = [\n '| Method | URL | Status |',\n '|--------|-----|--------|',\n ...requests.map((request) => `| ${cell(request.method)} | ${cell(shorten(request.url, 60))} | ${request.status === 0 ? 'ERR' : request.status} |`),\n ].join('\\n');\n if (failed.length) {\n const lines = failed.map((request) => {\n const reason = request.status === 0 ? request.error || request.statusText : `${request.status} ${request.statusText}`.trim();\n const body = request.responseBody ? `\\n \\`${shorten(request.responseBody, 200).replace(/`/g, \"'\").replace(/\\s+/g, ' ')}\\`` : '';\n return `- ${request.method} ${request.url.slice(0, 60)} → ${reason}${body}`;\n });\n content += `\\n\\n**Fehlerhafte Requests:**\\n${lines.join('\\n')}`;\n }\n technical.push(`### Network Requests (${requests.length})\\n\\n${content}`);\n }\n if (technical.length) {\n sections.push(`---\\n\\n## Technische Details\\n\\n${technical.join('\\n\\n')}`);\n }\n\n const notes: string[] = [];\n if (report.attachments.length) {\n const screenshots = report.attachments.filter((file) => file.isScreenshot).length;\n const others = report.attachments.length - screenshots;\n notes.push(\n screenshots && others ? `*${screenshots} Screenshot(s) und ${others} weitere Anhänge*` : screenshots ? `*${screenshots} Screenshot(s) angehängt*` : `*${others} Anhänge*`,\n );\n }\n if (report.skippedAttachments) {\n notes.push(`*${report.skippedAttachments} Anhang/Anhänge nicht übernommen (zu groß oder zu viele)*`);\n }\n if (notes.length) {\n sections.push(`---\\n\\n${notes.join('\\n\\n')}`);\n }\n\n return { description: sections.join('\\n\\n'), title: `${report.type.toUpperCase()}: ${report.title}` };\n}\n\n/** Escape a Markdown table cell. */\nfunction cell(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\|/g, '\\\\|')\n .replace(/\\s*[\\r\\n]+\\s*/g, ' ');\n}\n\nfunction shorten(value: string, max: number): string {\n return value.length > max ? `${value.slice(0, max - 3)}...` : value;\n}\n\nfunction eventType(type: string): string {\n return type.replace(/_/g, ' ').replace(/\\b\\w/g, (letter) => letter.toUpperCase());\n}\n\nfunction time(timestamp: string, timeZone: string): string {\n const date = new Date(timestamp);\n if (Number.isNaN(date.getTime())) {\n return '';\n }\n try {\n return date.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone });\n } catch {\n return date.toISOString().slice(11, 19);\n }\n}\n\nfunction eventDetails(event: LtBugServerReport['userInteractions'][number]): string {\n const metadata = event.metadata;\n if (!metadata) {\n return '';\n }\n if (metadata.toUrl) {\n return `→ ${metadata.toUrl.slice(0, 50)}`;\n }\n if (metadata.text) {\n return metadata.text.slice(0, 40);\n }\n if (metadata.errorMessage) {\n return `Error: ${metadata.errorMessage.slice(0, 40)}`;\n }\n if (metadata.key) {\n return `Key: ${metadata.key}`;\n }\n return '';\n}\n","/*\n * Redaction of secrets in reported data (headers, URLs, bodies, console text), applied once more on the server before\n * anything is written to Linear.\n *\n * Identical copy of `bug/src/lib/redact.ts` (the browser entry), kept apart because `@lenne.tech/ng-base/bug/server`\n * must not import browser code; `redact-parity.spec.ts` keeps both copies in sync.\n */\n\n/** Replacement of every redacted value. */\nexport const LT_BUG_REDACTED = '[redacted]';\n\n/** Headers whose values are never recorded (compared case-insensitively). */\nexport const LT_BUG_SENSITIVE_HEADERS: readonly string[] = Object.freeze([\n 'api-key',\n 'authorization',\n 'cookie',\n 'proxy-authorization',\n 'set-cookie',\n 'x-access-token',\n 'x-api-key',\n 'x-auth-token',\n 'x-csrf-token',\n 'x-system-setup-token',\n 'x-xsrf-token',\n]);\n\n/**\n * Path segments whose request and response bodies are never recorded: nest-server IAM (`/iam/…`: passwords, tokens,\n * 2FA codes, reset and verify tokens), the first-admin setup (`/system-setup/…`) and other auth-like endpoints.\n */\nexport const LT_BUG_BODY_EXCLUDED_SEGMENTS: readonly string[] = Object.freeze([\n 'auth',\n 'iam',\n 'login',\n 'logout',\n 'oauth',\n 'oauth2',\n 'passkey',\n 'reset-password',\n 'sign-in',\n 'sign-up',\n 'signin',\n 'signup',\n 'system-setup',\n 'token',\n 'two-factor',\n 'verify-email',\n]);\n\n/** Key fragments that mark a value as secret (key lower-cased, `-`/`_` removed). */\nconst SENSITIVE_KEY_PARTS = [\n 'apikey',\n 'authorization',\n 'backupcode',\n 'cardnumber',\n 'cookie',\n 'credential',\n 'creditcard',\n 'passwd',\n 'password',\n 'privatekey',\n 'recoverycode',\n 'secret',\n 'signature',\n 'token',\n 'totp',\n 'twofactor',\n 'verificationcode',\n];\n\n/** Keys that are secret as a whole (`code` from OAuth / email links, `otp`, `pin` …). */\nconst SENSITIVE_KEYS = new Set(['auth', 'code', 'csc', 'cvc', 'cvv', 'iban', 'jwt', 'otp', 'pin', 'pwd', 'sessionid', 'sid', 'ssn']);\n\nconst SENSITIVE_HEADER_SET = new Set(LT_BUG_SENSITIVE_HEADERS);\nconst BODY_EXCLUDED = new RegExp(`/(?:${LT_BUG_BODY_EXCLUDED_SEGMENTS.map((segment) => segment.replace(/[-/]/g, '\\\\$&')).join('|')})(?:[/?#;]|$)`, 'i');\n\nconst USERINFO = /\\b([a-z][a-z\\d+.-]*:\\/\\/)[^\\s/?#@:]+:[^\\s/?#@]+@/gi;\nconst JWT = /\\beyJ[\\w-]{5,}\\.[\\w-]{5,}\\.[\\w-]*/g;\nconst BEARER = /\\b(Bearer)\\s+(?=[\\w\\-.~+/]*[\\d.\\-_~+/])[\\w\\-.~+/]{8,}=*/g;\nconst PATH_TOKEN = /(\\/(?:activate|confirm-email|email-verification|invitation|invite|magic-link|reset-password|verify-email)\\/)[^/?#\\s\"'<>]+/gi;\nconst PARAM = /(^|[?&#;\\s\"'(,{]|%3[fF]|%26|%23)((?:[\\w.\\-~[\\]]|%5[bBdD])+)(=|%3[dD])([^&#;\\s\"'<>)}]*?)(?=$|[&#;\\s\"'<>)}]|%26|%23)/g;\nconst JSON_PAIR = /(\"((?:[^\"\\\\]|\\\\.)*)\"\\s*:\\s*)(\"(?:[^\"\\\\]|\\\\.)*\"|-?\\d[\\d.eE+-]*|true|false|null)/g;\nconst JSON_PAIR_OPEN = /(\"((?:[^\"\\\\]|\\\\.)*)\"\\s*:\\s*)\"(?:[^\"\\\\]|\\\\.)*$/;\nconst LABELLED = /\\b(api[-_]?key|otp|passwd|password|secret|token)(\\s*:\\s*)(\"[^\"]*\"|'[^']*'|[^\\s,;]+)/gi;\n\n/** Whether a header carries credentials (`Authorization`, `Cookie`, `Set-Cookie`, `X-System-Setup-Token` …). */\nexport function isLtSensitiveHeader(name: string): boolean {\n return SENSITIVE_HEADER_SET.has(name.trim().toLowerCase());\n}\n\n/**\n * Whether an object key / form field name / query parameter holds a secret (`password`, `newPassword`, `token`,\n * `access_token`, `code`, `otp`, `user[password]` …).\n */\nexport function isLtSensitiveKey(key: string): boolean {\n const decoded = safeDecode(key);\n const last =\n decoded\n .split(/[[\\].]/)\n .filter(Boolean)\n .pop() ?? decoded;\n const normalized = last.toLowerCase().replace(/[-_\\s]/g, '');\n return SENSITIVE_KEYS.has(normalized) || SENSITIVE_KEY_PARTS.some((part) => normalized.includes(part));\n}\n\n/** Whether request and response bodies of `url` must not be recorded (see {@link LT_BUG_BODY_EXCLUDED_SEGMENTS}). */\nexport function isLtBodyExcludedUrl(url: string): boolean {\n const path = url.replace(/^[a-z][a-z\\d+.-]*:\\/\\/[^/?#]*/i, '').split(/[?#]/)[0] ?? '';\n return BODY_EXCLUDED.test(path);\n}\n\n/**\n * Redact secrets in free text and URLs: query/hash/form parameters (`token`, `code`, `password`, `secret`, `otp`, …,\n * also inside nested and percent-encoded `callbackURL` values), tokens in reset/verify paths, URL credentials,\n * `Bearer` tokens, JWTs, JSON pairs with secret keys and `password: …` labels.\n */\nexport function redactLtText(text: string): string {\n if (!text) {\n return text;\n }\n let result = text.replace(USERINFO, `$1${LT_BUG_REDACTED}@`).replace(JWT, LT_BUG_REDACTED).replace(BEARER, `$1 ${LT_BUG_REDACTED}`).replace(PATH_TOKEN, `$1${LT_BUG_REDACTED}`);\n result = redactParams(result, 0);\n result = result.replace(JSON_PAIR, (match, prefix: string, key: string) => (isLtSensitiveKey(key) ? `${prefix}\"${LT_BUG_REDACTED}\"` : match));\n result = result.replace(JSON_PAIR_OPEN, (match, prefix: string, key: string) => (isLtSensitiveKey(key) ? `${prefix}\"${LT_BUG_REDACTED}\"` : match));\n return result.replace(LABELLED, `$1$2${LT_BUG_REDACTED}`);\n}\n\n/** Redact a URL (alias of {@link redactLtText}, which handles URLs inside text as well). */\nexport function redactLtUrl(url: string): string {\n return redactLtText(url);\n}\n\n/** Deep copy of a parsed JSON value with secret keys replaced and every string redacted. */\nexport function redactLtValue(value: unknown, depth = 0): unknown {\n if (typeof value === 'string') {\n return redactLtText(value);\n }\n if (depth > 20 || value === null || typeof value !== 'object') {\n return depth > 20 ? LT_BUG_REDACTED : value;\n }\n if (Array.isArray(value)) {\n return value.map((item) => redactLtValue(item, depth + 1));\n }\n const result: Record<string, unknown> = {};\n for (const [key, item] of Object.entries(value)) {\n result[key] = isLtSensitiveKey(key) ? LT_BUG_REDACTED : redactLtValue(item, depth + 1);\n }\n return result;\n}\n\n/** Redact and truncate a request/response body: JSON deep, everything else (form data, text) as text. */\nexport function redactLtBody(body: string, maxLength = 1000): string {\n const input = body.length > 20_000 ? body.slice(0, 20_000) : body;\n const trimmed = input.trim();\n let redacted: string;\n if (trimmed.startsWith('{') || trimmed.startsWith('[')) {\n try {\n redacted = JSON.stringify(redactLtValue(JSON.parse(trimmed)));\n } catch {\n redacted = redactLtText(input);\n }\n } else {\n redacted = redactLtText(input);\n }\n return truncateLt(redacted, maxLength);\n}\n\n/** Copy of a header map with credential headers replaced by `[redacted]` and the other values redacted as text. */\nexport function redactLtHeaders(headers: Record<string, string>): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [name, value] of Object.entries(headers)) {\n result[name] = isLtSensitiveHeader(name) ? LT_BUG_REDACTED : redactLtText(String(value));\n }\n return result;\n}\n\n/** Cut `text` to `maxLength` characters with a marker naming the dropped length. */\nexport function truncateLt(text: string, maxLength: number): string {\n return text.length <= maxLength ? text : `${text.slice(0, maxLength)}... [truncated ${text.length - maxLength} characters]`;\n}\n\nfunction redactParams(text: string, depth: number): string {\n return text.replace(PARAM, (match, separator: string, key: string, equals: string, value: string) => {\n if (isLtSensitiveKey(key)) {\n return `${separator}${key}${equals}${value ? LT_BUG_REDACTED : ''}`;\n }\n // Nested URLs (`callbackURL=https://app/verify?token=…`, also percent-encoded) carry their own parameters.\n if (depth < 3 && /=|%3[dD]/.test(value)) {\n return `${separator}${key}${equals}${redactParams(value, depth + 1)}`;\n }\n return match;\n });\n}\n\nfunction safeDecode(value: string): string {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n","/// <reference types=\"node\" />\nimport { isLtBodyExcludedUrl, redactLtBody, redactLtText, redactLtUrl, truncateLt } from './redact';\n\n/** Report types (mapped onto Linear labels). */\nexport const LT_BUG_REPORT_TYPES = ['bug', 'feature', 'enhancement', 'other'] as const;\n\n/** Kind of report. */\nexport type LtBugServerReportType = (typeof LT_BUG_REPORT_TYPES)[number];\n\n/** Size limits applied to an incoming report (texts are cut, lists keep their newest entries). */\nexport const LT_BUG_REPORT_LIMITS = Object.freeze({\n /** Attachments per report (including the screenshot). */\n attachments: 10,\n /** Characters of one attachment / screenshot data URL (about 11 MB decoded). */\n attachmentDataUrl: 15_000_000,\n consoleLogs: 100,\n description: 20_000,\n networkRequests: 100,\n text: 10_000,\n title: 255,\n userInteractions: 200,\n});\n\n/** A decoded file of a report. */\nexport interface LtBugServerFile {\n contentType: string;\n data: Buffer;\n isScreenshot: boolean;\n name: string;\n}\n\n/** Console entry after validation. */\nexport interface LtBugServerConsoleLog {\n level: string;\n message: string[];\n stack?: string;\n timestamp: string;\n}\n\n/** Network entry after validation (headers and request bodies are dropped). */\nexport interface LtBugServerNetworkRequest {\n error?: string;\n method: string;\n responseBody?: string;\n status: number;\n statusText: string;\n timestamp: string;\n url: string;\n}\n\n/** User journey entry after validation. */\nexport interface LtBugServerInteraction {\n metadata?: { errorMessage?: string; key?: string; text?: string; toUrl?: string; url?: string; value?: string };\n target: string;\n timestamp: string;\n type: string;\n}\n\n/** Browser details after validation. */\nexport interface LtBugServerBrowserInfo {\n browser?: { name?: string; version?: string };\n language?: string;\n os?: { name?: string; version?: string };\n platform?: string;\n timestamp?: string;\n url?: string;\n userAgent?: string;\n viewport?: { height: number; width: number };\n}\n\n/** A validated, size-capped and redacted report. */\nexport interface LtBugServerReport {\n attachments: LtBugServerFile[];\n browserInfo?: LtBugServerBrowserInfo;\n consoleLogs: LtBugServerConsoleLog[];\n description: string;\n expectedBehavior?: string;\n networkRequests: LtBugServerNetworkRequest[];\n /** Attachments dropped because they exceeded the limits. */\n skippedAttachments: number;\n stepsToReproduce?: string;\n title: string;\n type: LtBugServerReportType;\n userInteractions: LtBugServerInteraction[];\n}\n\n/** Result of {@link validateLtBugReport}. */\nexport type LtBugReportValidation = { error: string; ok: false } | { ok: true; report: LtBugServerReport };\n\n/** Message for a report without title, type or description (same text as `@lenne.tech/bug.lt`). */\nexport const LT_BUG_MISSING_FIELDS = 'Missing required fields: title, type, and description are required';\n\nconst DATA_URL = /^data:([\\w.+-]+\\/[\\w.+-]+)?((?:;[\\w.+-]+=[^;,]*)*);base64,/i;\nconst BASE64 = /^[A-Za-z0-9+/]*={0,2}$/;\nconst MIME = /^[\\w.+-]+\\/[\\w.+-]+$/;\n\n/**\n * Validate an incoming report by hand: title, type and description are required strings, `type` one of\n * {@link LT_BUG_REPORT_TYPES}; files must be base64 `data:` URLs (never fetched: no URL is ever requested by the server).\n * Texts are cut, lists keep their newest entries, files above the limit are dropped and counted, everything is redacted.\n */\nexport function validateLtBugReport(body: unknown): LtBugReportValidation {\n if (!isRecord(body)) {\n return invalid('The bug report must be a JSON object');\n }\n const title = typeof body['title'] === 'string' ? body['title'].trim() : '';\n const description = typeof body['description'] === 'string' ? body['description'].trim() : '';\n const type = body['type'];\n if (!title || !description || typeof type !== 'string' || !type) {\n return invalid(LT_BUG_MISSING_FIELDS);\n }\n if (!(LT_BUG_REPORT_TYPES as readonly string[]).includes(type)) {\n return invalid(`type must be one of ${LT_BUG_REPORT_TYPES.join(', ')}`);\n }\n for (const key of ['expectedBehavior', 'stepsToReproduce', 'screenshot']) {\n if (body[key] !== undefined && body[key] !== null && typeof body[key] !== 'string') {\n return invalid(`${key} must be a string`);\n }\n }\n for (const key of ['attachments', 'consoleLogs', 'networkRequests', 'userInteractions']) {\n if (body[key] !== undefined && body[key] !== null && !Array.isArray(body[key])) {\n return invalid(`${key} must be an array`);\n }\n }\n if (body['browserInfo'] !== undefined && body['browserInfo'] !== null && !isRecord(body['browserInfo'])) {\n return invalid('browserInfo must be an object');\n }\n\n const files: LtBugServerFile[] = [];\n let skippedAttachments = 0;\n const rawAttachments = (body['attachments'] as unknown[] | undefined) ?? [];\n for (const [index, raw] of rawAttachments.entries()) {\n if (!isRecord(raw) || typeof raw['data'] !== 'string') {\n return invalid(`attachments[${index}] needs a data URL`);\n }\n const file = decodeDataUrl(raw['data'], typeof raw['type'] === 'string' ? raw['type'] : undefined);\n if (file === 'invalid') {\n return invalid(`attachments[${index}] is not a base64 data URL`);\n }\n if (file === 'too-large' || files.length >= LT_BUG_REPORT_LIMITS.attachments || file.data.length === 0) {\n skippedAttachments++;\n continue;\n }\n files.push({ ...file, isScreenshot: raw['isScreenshot'] === true, name: fileName(raw['name'], index) });\n }\n const screenshot = typeof body['screenshot'] === 'string' && body['screenshot'] ? body['screenshot'] : undefined;\n if (screenshot && !files.some((file) => file.isScreenshot)) {\n const file = decodeDataUrl(screenshot, undefined);\n if (file === 'invalid' || (file !== 'too-large' && !file.contentType.startsWith('image/'))) {\n return invalid('screenshot is not a base64 image data URL');\n }\n if (file === 'too-large' || files.length >= LT_BUG_REPORT_LIMITS.attachments) {\n skippedAttachments++;\n } else {\n files.push({ ...file, isScreenshot: true, name: `screenshot-${Date.now()}.png` });\n }\n }\n\n return {\n ok: true,\n report: {\n attachments: files,\n browserInfo: browserInfo(body['browserInfo']),\n consoleLogs: newest(body['consoleLogs'], LT_BUG_REPORT_LIMITS.consoleLogs).flatMap(consoleLog),\n description: cleanText(description, LT_BUG_REPORT_LIMITS.description),\n expectedBehavior: optionalText(body['expectedBehavior']),\n networkRequests: newest(body['networkRequests'], LT_BUG_REPORT_LIMITS.networkRequests).flatMap(networkRequest),\n skippedAttachments,\n stepsToReproduce: optionalText(body['stepsToReproduce']),\n title: cleanText(title, LT_BUG_REPORT_LIMITS.title).replace(/\\s+/g, ' '),\n type: type as LtBugServerReportType,\n userInteractions: newest(body['userInteractions'], LT_BUG_REPORT_LIMITS.userInteractions).flatMap(interaction),\n },\n };\n}\n\nfunction invalid(error: string): LtBugReportValidation {\n return { error, ok: false };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction newest(value: unknown, limit: number): unknown[] {\n return Array.isArray(value) ? value.slice(-limit) : [];\n}\n\nfunction cleanText(text: string, max: number): string {\n return truncateLt(redactLtText(text.length > max * 2 ? text.slice(0, max * 2) : text), max);\n}\n\nfunction optionalText(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? cleanText(value.trim(), LT_BUG_REPORT_LIMITS.text) : undefined;\n}\n\nfunction str(value: unknown, max: number): string | undefined {\n return typeof value === 'string' ? cleanText(value, max) : undefined;\n}\n\nfunction num(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\n/** Decode a base64 data URL; never fetches anything. */\nfunction decodeDataUrl(dataUrl: string, declaredType: string | undefined): Omit<LtBugServerFile, 'isScreenshot' | 'name'> | 'invalid' | 'too-large' {\n const header = DATA_URL.exec(dataUrl.slice(0, 300));\n if (!header) {\n return 'invalid';\n }\n if (dataUrl.length > LT_BUG_REPORT_LIMITS.attachmentDataUrl) {\n return 'too-large';\n }\n const payload = dataUrl.slice(header[0].length);\n if (!BASE64.test(payload)) {\n return 'invalid';\n }\n const fromUrl = header[1]?.toLowerCase();\n const contentType = declaredType && MIME.test(declaredType) ? declaredType.toLowerCase() : fromUrl && MIME.test(fromUrl) ? fromUrl : 'application/octet-stream';\n return { contentType, data: Buffer.from(payload, 'base64') };\n}\n\n/** File name without path parts and control characters. */\nfunction fileName(value: unknown, index: number): string {\n const raw = typeof value === 'string' ? value : '';\n const base = raw.split(/[/\\\\]/).pop() ?? '';\n const name = Array.from(base)\n .filter((char) => char.charCodeAt(0) >= 32 && char.charCodeAt(0) !== 127)\n .join('')\n .trim();\n return (name || `attachment-${index + 1}`).slice(0, 200);\n}\n\nfunction browserInfo(value: unknown): LtBugServerBrowserInfo | undefined {\n if (!isRecord(value)) {\n return undefined;\n }\n const pair = (source: unknown) => (isRecord(source) ? { name: str(source['name'], 100), version: str(source['version'], 100) } : undefined);\n const viewport = isRecord(value['viewport']) ? { height: num(value['viewport']['height']) ?? 0, width: num(value['viewport']['width']) ?? 0 } : undefined;\n return {\n browser: pair(value['browser']),\n language: str(value['language'], 50),\n os: pair(value['os']),\n platform: str(value['platform'], 100),\n timestamp: str(value['timestamp'], 50),\n url: typeof value['url'] === 'string' ? truncateLt(redactLtUrl(value['url'].slice(0, 4000)), 2000) : undefined,\n userAgent: str(value['userAgent'], 500),\n viewport,\n };\n}\n\nfunction consoleLog(value: unknown): LtBugServerConsoleLog[] {\n if (!isRecord(value)) {\n return [];\n }\n const parts = Array.isArray(value['message']) ? value['message'] : [value['message']];\n return [\n {\n level: ['debug', 'error', 'info', 'log', 'warn'].includes(String(value['level'])) ? String(value['level']) : 'log',\n message: parts\n .slice(0, 20)\n .map((part) => str(typeof part === 'string' ? part : String(part ?? ''), 2000) ?? '')\n .filter(Boolean),\n stack: str(value['stack'], 5000),\n timestamp: str(value['timestamp'], 50) ?? '',\n },\n ];\n}\n\nfunction networkRequest(value: unknown): LtBugServerNetworkRequest[] {\n if (!isRecord(value)) {\n return [];\n }\n const url = typeof value['url'] === 'string' ? value['url'].slice(0, 4000) : '';\n const responseBody = typeof value['responseBody'] === 'string' && !isLtBodyExcludedUrl(url) ? redactLtBody(value['responseBody'], 1000) : undefined;\n return [\n {\n error: str(value['error'], 500),\n method: (str(value['method'], 16) ?? 'GET').toUpperCase(),\n responseBody,\n status: num(value['status']) ?? 0,\n statusText: str(value['statusText'], 200) ?? '',\n timestamp: str(value['timestamp'], 50) ?? '',\n url: truncateLt(redactLtUrl(url), 2000),\n },\n ];\n}\n\nfunction interaction(value: unknown): LtBugServerInteraction[] {\n if (!isRecord(value)) {\n return [];\n }\n const metadata = isRecord(value['metadata']) ? value['metadata'] : undefined;\n return [\n {\n metadata: metadata\n ? {\n errorMessage: str(metadata['errorMessage'], 500),\n key: str(metadata['key'], 50),\n text: str(metadata['text'], 500),\n toUrl: str(metadata['toUrl'], 2000),\n url: str(metadata['url'], 2000),\n value: str(metadata['value'], 500),\n }\n : undefined,\n target: str(value['target'], 200) ?? '',\n timestamp: str(value['timestamp'], 50) ?? '',\n type: str(value['type'], 50) ?? 'click',\n },\n ];\n}\n","/// <reference types=\"node\" />\nimport type { IncomingMessage, ServerResponse } from 'node:http';\n\nimport type { LtEnv, LtNodeMiddleware } from '@lenne.tech/ng-base/server';\n\nimport { LT_BUG_LABELS, LtLinearClient } from './linear-client';\nimport { formatLtBugReportForLinear } from './linear-format';\nimport { LT_BUG_REDACTED, redactLtText } from './redact';\nimport { validateLtBugReport, type LtBugServerReport } from './validate';\n\n/** Default endpoint (the same as `@lenne.tech/bug.lt` and `provideLtBug()`). */\nexport const LT_BUG_SERVER_DEFAULT_ENDPOINT = '/_bug-lt/report';\n\n/** Default request body limit (30 MB: one 10 MB file plus screenshot fit, base64 included). */\nexport const LT_BUG_DEFAULT_MAX_BODY_BYTES = 30 * 1024 * 1024;\n\nconst PREFIX = '[@lenne.tech/ng-base/bug/server]';\n\n/** Options of {@link ltBugReport}. */\nexport interface LtBugReportHandlerOptions {\n /**\n * Force the endpoint on or off (env `NG_PUBLIC_LT_BUG_ENABLED` wins). Default: on when `NG_PUBLIC_APP_ENV` is set and\n * not `production`; without `NG_PUBLIC_APP_ENV` it stays off (fail closed). Off answers `404`.\n */\n enabled?: boolean;\n /** Route (default `/_bug-lt/report`); must match `provideLtBug({ endpoint })`. */\n endpoint?: string;\n /** Environment (default `process.env`). */\n env?: LtEnv;\n /** `fetch` used for Linear (default the global `fetch`; for tests or an outbound proxy). */\n fetch?: typeof fetch;\n /** Linear API key; env `NG_LINEAR_API_KEY` wins. Server only, never sent to the browser. */\n linearApiKey?: string;\n /** Linear project name (optional); env `NG_LINEAR_PROJECT_NAME` wins. */\n linearProjectName?: string;\n /** Linear team name or key; env `NG_LINEAR_TEAM_NAME` wins. */\n linearTeamName?: string;\n /** Logger for server-side diagnostics (default `console`); messages are redacted and never contain the API key. */\n logger?: Pick<Console, 'error' | 'warn'>;\n /** Request body limit in bytes (default {@link LT_BUG_DEFAULT_MAX_BODY_BYTES}); larger bodies get `413`. */\n maxBodyBytes?: number;\n /** Reports per client address and window (default 20 per 10 minutes); `false` turns the limit off. */\n rateLimit?: false | { max: number; windowMs: number };\n /** Time zone of the times in the issue (default `Europe/Berlin`). */\n timeZone?: string;\n}\n\n/** Linear settings of one request. */\ninterface LinearConfig {\n apiKey: string;\n projectName?: string;\n teamName: string;\n}\n\nclass HttpError extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n }\n}\n\n/**\n * Whether the bug report endpoint is on, in this order:\n *\n * 1. `NG_PUBLIC_LT_BUG_ENABLED` (`true`/`false`), the same switch the browser part reads, so one variable turns the\n * reporter on or off per stage on both sides.\n * 2. The `enabled` option.\n * 3. On when `NG_PUBLIC_APP_ENV` is set and neither `production` nor `prod`; without `NG_PUBLIC_APP_ENV` it is off\n * (fail closed, also locally: set e.g. `local`).\n */\nexport function isLtBugServerEnabled(enabled: boolean | undefined, env: LtEnv): boolean {\n const switchValue = env['NG_PUBLIC_LT_BUG_ENABLED']?.trim() ?? '';\n if (/^(true|1|yes|on)$/i.test(switchValue)) {\n return true;\n }\n if (/^(false|0|no|off)$/i.test(switchValue)) {\n return false;\n }\n if (typeof enabled === 'boolean') {\n return enabled;\n }\n const appEnv = env['NG_PUBLIC_APP_ENV']?.trim().toLowerCase();\n return !!appEnv && appEnv !== 'production' && appEnv !== 'prod';\n}\n\n/**\n * `POST /_bug-lt/report`: validates a report of `provideLtBug()` / `<lt-bug-report>` and creates a Linear issue (team\n * and optional project resolved by name, label per type found or created, screenshot and attachments uploaded and\n * linked), the counterpart of the Nitro handler of `@lenne.tech/bug.lt`.\n *\n * - Credentials only here: `NG_LINEAR_API_KEY`, `NG_LINEAR_TEAM_NAME`, `NG_LINEAR_PROJECT_NAME` (or the options).\n * - Off (`404`) when `NG_PUBLIC_APP_ENV` is `production` or missing, unless `NG_PUBLIC_LT_BUG_ENABLED` or `enabled` turn it on.\n * - JSON only (`415` otherwise, which also blocks simple cross-site form posts), body limit (`413`), per-client rate\n * limit (`429`), hand-written validation (`400`), files only as base64 data URLs (no URL is ever fetched).\n * - Redacts tokens, passwords and auth bodies once more before writing to Linear; answers never contain the API key or\n * Linear's error messages (`502` with a generic text, details only in the server log).\n *\n * Register it before `express.json()` (which would reject large reports with its 100 kB default) and before the Angular\n * handler.\n *\n * @example server.ts\n * app.use(ltBugReport());\n */\nexport function ltBugReport(options: LtBugReportHandlerOptions = {}): LtNodeMiddleware {\n const endpoint = options.endpoint ?? LT_BUG_SERVER_DEFAULT_ENDPOINT;\n const limiter = createRateLimiter(options.rateLimit === undefined ? { max: 20, windowMs: 10 * 60 * 1000 } : options.rateLimit);\n const logger = options.logger ?? console;\n return (req, res, next) => {\n if ((req.url ?? '').split('?')[0] !== endpoint) {\n next();\n return;\n }\n const env = options.env ?? processEnv();\n if (!isLtBugServerEnabled(options.enabled, env)) {\n sendJson(res, 404, { error: 'Not Found', success: false });\n return;\n }\n handle(req, res, env, options, limiter).catch((error: unknown) => {\n const status = error instanceof HttpError ? error.status : 500;\n if (!(error instanceof HttpError)) {\n logger.error(`${PREFIX} Unexpected error:`, sanitize(error, linearConfig(env, options)?.apiKey));\n }\n if (!res.headersSent) {\n sendJson(res, status, { error: error instanceof HttpError ? error.message : 'Internal server error while processing bug report', success: false });\n }\n });\n };\n}\n\nasync function handle(req: IncomingMessage, res: ServerResponse, env: LtEnv, options: LtBugReportHandlerOptions, limiter: RateLimiter): Promise<void> {\n const logger = options.logger ?? console;\n if (req.method !== 'POST') {\n res.setHeader('allow', 'POST');\n throw new HttpError(405, 'Method Not Allowed');\n }\n if (!/^application\\/json\\b/i.test(headerValue(req.headers['content-type']) ?? '')) {\n throw new HttpError(415, 'Content-Type must be application/json');\n }\n const retryAfter = limiter(clientKey(req));\n if (retryAfter > 0) {\n res.setHeader('retry-after', String(Math.ceil(retryAfter / 1000)));\n throw new HttpError(429, 'Too many bug reports, please try again later');\n }\n const config = linearConfig(env, options);\n if (!config) {\n const missing = [\n !pick(env, 'NG_LINEAR_API_KEY', options.linearApiKey) && 'NG_LINEAR_API_KEY',\n !pick(env, 'NG_LINEAR_TEAM_NAME', options.linearTeamName) && 'NG_LINEAR_TEAM_NAME',\n ].filter(Boolean);\n logger.error(`${PREFIX} Linear is not configured: set ${missing.join(' and ')} on the server.`);\n throw new HttpError(500, 'Bug reporting is not configured on the server');\n }\n\n const body = await readJsonBody(req, options.maxBodyBytes ?? LT_BUG_DEFAULT_MAX_BODY_BYTES);\n const validation = validateLtBugReport(body);\n if (!validation.ok) {\n throw new HttpError(400, validation.error);\n }\n\n let result: { identifier?: string; issueId: string; issueUrl?: string };\n try {\n result = await createLtLinearIssue(validation.report, config, { fetch: options.fetch, logger, timeZone: options.timeZone });\n } catch (error) {\n if (error instanceof LtBugConfigError) {\n logger.error(`${PREFIX} ${sanitize(error, config.apiKey)}`);\n throw new HttpError(500, 'Bug reporting is not configured on the server');\n }\n logger.error(`${PREFIX} Linear rejected the bug report:`, sanitize(error, config.apiKey));\n throw new HttpError(502, 'Linear rejected the bug report');\n }\n sendJson(res, 200, { identifier: result.identifier, issueId: result.issueId, issueUrl: result.issueUrl, message: 'Bug report submitted successfully', success: true });\n}\n\n/** Team or project setup does not match the Linear workspace. */\nexport class LtBugConfigError extends Error {}\n\n/**\n * Create the Linear issue of a validated report: resolve the team (required) and project (optional), find or create\n * the type label, create the issue, then upload and link every file (failures are logged and skipped).\n */\nexport async function createLtLinearIssue(\n report: LtBugServerReport,\n config: LinearConfig,\n options: { fetch?: typeof fetch; logger?: Pick<Console, 'error' | 'warn'>; timeZone?: string } = {},\n): Promise<{ identifier?: string; issueId: string; issueUrl?: string }> {\n const logger = options.logger ?? console;\n const client = new LtLinearClient({ apiKey: config.apiKey, fetch: options.fetch });\n\n const team = await client.findTeam(config.teamName);\n if (!team) {\n const available = (await client.getTeams().catch(() => [])).map((candidate) => candidate.name).join(', ');\n throw new LtBugConfigError(`Linear team \"${config.teamName}\" not found (NG_LINEAR_TEAM_NAME). Available teams: ${available || 'none'}`);\n }\n\n let projectId: string | undefined;\n if (config.projectName) {\n try {\n projectId = (await client.findProject(config.projectName))?.id;\n if (!projectId) {\n logger.warn(`${PREFIX} Linear project \"${config.projectName}\" not found (NG_LINEAR_PROJECT_NAME); creating the issue without project.`);\n }\n } catch (error) {\n logger.warn(`${PREFIX} Could not resolve the Linear project, continuing without:`, sanitize(error, config.apiKey));\n }\n }\n\n let labelId: string | null = null;\n const label = LT_BUG_LABELS[report.type];\n if (label) {\n try {\n labelId = (await client.findLabel(team.id, label.name)) ?? (await client.createLabel(team.id, label.name, label.color));\n } catch (error) {\n logger.warn(`${PREFIX} Could not find or create the label \"${label.name}\", continuing without:`, sanitize(error, config.apiKey));\n }\n }\n\n const text = formatLtBugReportForLinear(report, { timeZone: options.timeZone });\n const issue = await client.createIssue({ ...text, labelIds: labelId ? [labelId] : undefined, projectId, teamId: team.id });\n\n for (const file of report.attachments) {\n try {\n const assetUrl = await client.uploadFile(file.data, file.name, file.contentType);\n await client.createAttachment(issue.id, assetUrl, file.name);\n } catch (error) {\n logger.warn(`${PREFIX} Could not upload the attachment \"${file.name}\", continuing without:`, sanitize(error, config.apiKey));\n }\n }\n return { identifier: issue.identifier, issueId: issue.id, issueUrl: issue.url };\n}\n\nfunction linearConfig(env: LtEnv, options: LtBugReportHandlerOptions): LinearConfig | null {\n const apiKey = pick(env, 'NG_LINEAR_API_KEY', options.linearApiKey);\n const teamName = pick(env, 'NG_LINEAR_TEAM_NAME', options.linearTeamName);\n if (!apiKey || !teamName) {\n return null;\n }\n return { apiKey, projectName: pick(env, 'NG_LINEAR_PROJECT_NAME', options.linearProjectName), teamName };\n}\n\n/** Env value (trimmed, non-empty) before the option. */\nfunction pick(env: LtEnv, name: string, fallback: string | undefined): string | undefined {\n const value = env[name]?.trim();\n return value || fallback?.trim() || undefined;\n}\n\n/** Error text for the server log: redacted, the API key replaced. */\nfunction sanitize(error: unknown, apiKey: string | undefined): string {\n let text = error instanceof Error ? error.message : String(error);\n if (apiKey) {\n text = text.split(apiKey).join(LT_BUG_REDACTED);\n }\n return redactLtText(text.replace(/lin_api_[A-Za-z0-9]+/g, LT_BUG_REDACTED));\n}\n\nasync function readJsonBody(req: IncomingMessage, limit: number): Promise<unknown> {\n const parsed = (req as IncomingMessage & { body?: unknown }).body;\n if (parsed !== undefined && parsed !== null && typeof parsed === 'object' && !Buffer.isBuffer(parsed)) {\n return parsed;\n }\n if (Number(headerValue(req.headers['content-length']) ?? 0) > limit) {\n req.resume();\n throw new HttpError(413, 'Bug report too large');\n }\n const text = await new Promise<string>((resolve, reject) => {\n const chunks: Buffer[] = [];\n let size = 0;\n let tooLarge = false;\n req.on('data', (chunk: Buffer) => {\n if (tooLarge) {\n return;\n }\n size += chunk.length;\n if (size > limit) {\n tooLarge = true;\n chunks.length = 0;\n reject(new HttpError(413, 'Bug report too large'));\n return;\n }\n chunks.push(chunk);\n });\n req.on('end', () => {\n if (!tooLarge) {\n resolve(Buffer.concat(chunks).toString('utf8'));\n }\n });\n req.on('error', (error) => reject(error));\n });\n try {\n return JSON.parse(text) as unknown;\n } catch {\n throw new HttpError(400, 'The bug report must be valid JSON');\n }\n}\n\ntype RateLimiter = (key: string) => number;\n\n/** Fixed-window limiter; returns 0 when allowed, else the ms until the window resets. */\nfunction createRateLimiter(limit: false | { max: number; windowMs: number }): RateLimiter {\n if (!limit) {\n return () => 0;\n }\n const windows = new Map<string, { count: number; resetAt: number }>();\n return (key) => {\n const now = Date.now();\n if (windows.size > 5000) {\n for (const [entryKey, entry] of windows) {\n if (entry.resetAt <= now) {\n windows.delete(entryKey);\n }\n }\n }\n const entry = windows.get(key);\n if (!entry || entry.resetAt <= now) {\n windows.set(key, { count: 1, resetAt: now + limit.windowMs });\n return 0;\n }\n entry.count++;\n return entry.count > limit.max ? entry.resetAt - now : 0;\n };\n}\n\n/** Client address: the last `X-Forwarded-For` entry (added by the own proxy), else the socket address. */\nfunction clientKey(req: IncomingMessage): string {\n const forwarded = headerValue(req.headers['x-forwarded-for'])?.split(',').pop()?.trim();\n return forwarded || req.socket?.remoteAddress || 'unknown';\n}\n\nfunction sendJson(res: ServerResponse, status: number, body: Record<string, unknown>): void {\n res.statusCode = status;\n res.setHeader('content-type', 'application/json; charset=utf-8');\n res.setHeader('cache-control', 'no-store');\n res.setHeader('x-content-type-options', 'nosniff');\n res.end(JSON.stringify(body));\n}\n\nfunction headerValue(value: string | string[] | undefined): string | undefined {\n return Array.isArray(value) ? value[0] : value;\n}\n\nfunction processEnv(): LtEnv {\n return (globalThis as { process?: { env?: LtEnv } }).process?.env ?? {};\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { a as LT_BUG_DEFAULTS, c as resolveLtBugOptions, i as registerLtBugEagerMessages, n as LtBugSubmitError, o as LT_BUG_DEFAULT_ENDPOINT, r as LT_BUG_SCREENSHOT_LOADER, s as LT_BUG_OPTIONS, t as LtBugReportService } from "./lenne.tech-ng-base-bug-bug-report-service-DWoTb_iw.mjs";
|
|
2
|
+
import { t as LT_BUG_UI_ATTRIBUTE } from "./lenne.tech-ng-base-bug-constants-Brm2KGHi.mjs";
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
import { Component, Injector, booleanAttribute, computed, effect, inject, input, signal, untracked } from "@angular/core";
|
|
5
|
+
import { LT_I18N_OPTIONS, LtI18nService } from "@lenne.tech/ng-base";
|
|
6
|
+
import { NgIcon, provideIcons } from "@ng-icons/core";
|
|
7
|
+
import { lucideBug, lucideLoaderCircle } from "@ng-icons/lucide";
|
|
8
|
+
export * from "@lenne.tech/ng-base/bug/provide";
|
|
9
|
+
const POSITION_CLASSES = {
|
|
10
|
+
"bottom-left": "bottom-4 left-4",
|
|
11
|
+
"bottom-right": "bottom-4 right-4",
|
|
12
|
+
"top-left": "top-4 left-4",
|
|
13
|
+
"top-right": "top-4 right-4"
|
|
14
|
+
};
|
|
15
|
+
var LtBugReport = class LtBugReport {
|
|
16
|
+
color = input(...ngDevMode ? [void 0, { debugName: "color" }] : /* istanbul ignore next */ []);
|
|
17
|
+
icon = input(...ngDevMode ? [void 0, { debugName: "icon" }] : /* istanbul ignore next */ []);
|
|
18
|
+
position = input(...ngDevMode ? [void 0, { debugName: "position" }] : /* istanbul ignore next */ []);
|
|
19
|
+
show = input(void 0, {
|
|
20
|
+
...ngDevMode ? { debugName: "show" } : /* istanbul ignore next */ {},
|
|
21
|
+
transform: (value) => value === void 0 ? void 0 : booleanAttribute(value)
|
|
22
|
+
});
|
|
23
|
+
text = input(...ngDevMode ? [void 0, { debugName: "text" }] : /* istanbul ignore next */ []);
|
|
24
|
+
bug = inject(LtBugReportService);
|
|
25
|
+
i18n = inject(LtI18nService);
|
|
26
|
+
injector = inject(Injector);
|
|
27
|
+
announcement = signal("", ...ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []);
|
|
28
|
+
visible = computed(() => this.bug.active && (this.show() ?? this.bug.options.autoShow), ...ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []);
|
|
29
|
+
positionClass = computed(() => POSITION_CLASSES[this.position() ?? this.bug.options.position] ?? POSITION_CLASSES["bottom-right"], ...ngDevMode ? [{ debugName: "positionClass" }] : /* istanbul ignore next */ []);
|
|
30
|
+
buttonColor = computed(() => this.color() || this.bug.options.buttonColor, ...ngDevMode ? [{ debugName: "buttonColor" }] : /* istanbul ignore next */ []);
|
|
31
|
+
buttonText = computed(() => this.text() ?? this.bug.options.buttonText ?? "", ...ngDevMode ? [{ debugName: "buttonText" }] : /* istanbul ignore next */ []);
|
|
32
|
+
buttonIcon = computed(() => this.icon() || this.bug.options.buttonIcon || "lucideBug", ...ngDevMode ? [{ debugName: "buttonIcon" }] : /* istanbul ignore next */ []);
|
|
33
|
+
constructor() {
|
|
34
|
+
this.bug.start();
|
|
35
|
+
registerLtBugEagerMessages(this.i18n, inject(LT_I18N_OPTIONS).messages);
|
|
36
|
+
effect(() => {
|
|
37
|
+
if (this.bug.openRequested() && this.bug.active) untracked(() => {
|
|
38
|
+
this.bug.openRequested.set(false);
|
|
39
|
+
this.openDialog();
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
async openDialog() {
|
|
44
|
+
if (!this.bug.active || this.bug.isOpen()) return false;
|
|
45
|
+
this.bug.isOpen.set(true);
|
|
46
|
+
this.announcement.set("");
|
|
47
|
+
try {
|
|
48
|
+
await this.bug.captureScreenshot();
|
|
49
|
+
const { openLtBugReportDialog } = await import("./lenne.tech-ng-base-bug-bug-report-dialog-BRzVmKJr.mjs");
|
|
50
|
+
const sent = await openLtBugReportDialog(this.injector).closed === true;
|
|
51
|
+
if (sent) this.announcement.set(this.i18n.t("lt.bug.successTitle"));
|
|
52
|
+
return sent;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
console.error("[@lenne.tech/ng-base/bug] Could not open the bug report dialog", error);
|
|
55
|
+
return false;
|
|
56
|
+
} finally {
|
|
57
|
+
this.bug.isOpen.set(false);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
61
|
+
minVersion: "12.0.0",
|
|
62
|
+
version: "22.2.4",
|
|
63
|
+
ngImport: i0,
|
|
64
|
+
type: LtBugReport,
|
|
65
|
+
deps: [],
|
|
66
|
+
target: i0.ɵɵFactoryTarget.Component
|
|
67
|
+
});
|
|
68
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
69
|
+
minVersion: "17.0.0",
|
|
70
|
+
version: "22.2.4",
|
|
71
|
+
type: LtBugReport,
|
|
72
|
+
isStandalone: true,
|
|
73
|
+
selector: "lt-bug-report",
|
|
74
|
+
inputs: {
|
|
75
|
+
color: {
|
|
76
|
+
classPropertyName: "color",
|
|
77
|
+
publicName: "color",
|
|
78
|
+
isSignal: true,
|
|
79
|
+
isRequired: false,
|
|
80
|
+
transformFunction: null
|
|
81
|
+
},
|
|
82
|
+
icon: {
|
|
83
|
+
classPropertyName: "icon",
|
|
84
|
+
publicName: "icon",
|
|
85
|
+
isSignal: true,
|
|
86
|
+
isRequired: false,
|
|
87
|
+
transformFunction: null
|
|
88
|
+
},
|
|
89
|
+
position: {
|
|
90
|
+
classPropertyName: "position",
|
|
91
|
+
publicName: "position",
|
|
92
|
+
isSignal: true,
|
|
93
|
+
isRequired: false,
|
|
94
|
+
transformFunction: null
|
|
95
|
+
},
|
|
96
|
+
show: {
|
|
97
|
+
classPropertyName: "show",
|
|
98
|
+
publicName: "show",
|
|
99
|
+
isSignal: true,
|
|
100
|
+
isRequired: false,
|
|
101
|
+
transformFunction: null
|
|
102
|
+
},
|
|
103
|
+
text: {
|
|
104
|
+
classPropertyName: "text",
|
|
105
|
+
publicName: "text",
|
|
106
|
+
isSignal: true,
|
|
107
|
+
isRequired: false,
|
|
108
|
+
transformFunction: null
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
host: { attributes: { "data-lt-bug-ui": "" } },
|
|
112
|
+
providers: [provideIcons({
|
|
113
|
+
lucideBug,
|
|
114
|
+
lucideLoaderCircle
|
|
115
|
+
})],
|
|
116
|
+
ngImport: i0,
|
|
117
|
+
template: "@if (visible()) {\n <button\n type=\"button\"\n class=\"fixed z-[9999] inline-flex cursor-pointer items-center justify-center gap-2 rounded-full font-medium text-white shadow-lg transition-shadow duration-200 outline-none hover:shadow-xl focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-offset-2 aria-disabled:cursor-wait print:hidden\"\n data-slot=\"bug-report-button\"\n aria-haspopup=\"dialog\"\n [class]=\"positionClass()\"\n [class.size-12]=\"!buttonText()\"\n [class.h-12]=\"!!buttonText()\"\n [class.px-5]=\"!!buttonText()\"\n [style.background-color]=\"buttonColor()\"\n [attr.aria-label]=\"buttonText() ? null : i18n.t('lt.bug.button')\"\n [attr.aria-busy]=\"bug.capturingScreenshot() || null\"\n [attr.aria-disabled]=\"bug.capturingScreenshot() || null\"\n (click)=\"openDialog()\"\n >\n @if (bug.capturingScreenshot()) {\n <ng-icon name=\"lucideLoaderCircle\" class=\"animate-spin text-2xl\" aria-hidden=\"true\" />\n } @else {\n <ng-icon class=\"text-2xl\" aria-hidden=\"true\" [name]=\"buttonIcon()\" />\n }\n @if (buttonText()) {\n <span>{{ bug.capturingScreenshot() ? i18n.t('lt.bug.capturing') : buttonText() }}</span>\n }\n </button>\n}\n<p class=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\" data-slot=\"bug-report-announcement\">{{ announcement() }}</p>\n",
|
|
118
|
+
dependencies: [{
|
|
119
|
+
kind: "component",
|
|
120
|
+
type: NgIcon,
|
|
121
|
+
selector: "ng-icon",
|
|
122
|
+
inputs: [
|
|
123
|
+
"name",
|
|
124
|
+
"svg",
|
|
125
|
+
"size",
|
|
126
|
+
"strokeWidth",
|
|
127
|
+
"color"
|
|
128
|
+
]
|
|
129
|
+
}]
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
133
|
+
minVersion: "12.0.0",
|
|
134
|
+
version: "22.2.4",
|
|
135
|
+
ngImport: i0,
|
|
136
|
+
type: LtBugReport,
|
|
137
|
+
decorators: [{
|
|
138
|
+
type: Component,
|
|
139
|
+
args: [{
|
|
140
|
+
selector: "lt-bug-report",
|
|
141
|
+
imports: [NgIcon],
|
|
142
|
+
providers: [provideIcons({
|
|
143
|
+
lucideBug,
|
|
144
|
+
lucideLoaderCircle
|
|
145
|
+
})],
|
|
146
|
+
host: { "data-lt-bug-ui": "" },
|
|
147
|
+
template: "@if (visible()) {\n <button\n type=\"button\"\n class=\"fixed z-[9999] inline-flex cursor-pointer items-center justify-center gap-2 rounded-full font-medium text-white shadow-lg transition-shadow duration-200 outline-none hover:shadow-xl focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-offset-2 aria-disabled:cursor-wait print:hidden\"\n data-slot=\"bug-report-button\"\n aria-haspopup=\"dialog\"\n [class]=\"positionClass()\"\n [class.size-12]=\"!buttonText()\"\n [class.h-12]=\"!!buttonText()\"\n [class.px-5]=\"!!buttonText()\"\n [style.background-color]=\"buttonColor()\"\n [attr.aria-label]=\"buttonText() ? null : i18n.t('lt.bug.button')\"\n [attr.aria-busy]=\"bug.capturingScreenshot() || null\"\n [attr.aria-disabled]=\"bug.capturingScreenshot() || null\"\n (click)=\"openDialog()\"\n >\n @if (bug.capturingScreenshot()) {\n <ng-icon name=\"lucideLoaderCircle\" class=\"animate-spin text-2xl\" aria-hidden=\"true\" />\n } @else {\n <ng-icon class=\"text-2xl\" aria-hidden=\"true\" [name]=\"buttonIcon()\" />\n }\n @if (buttonText()) {\n <span>{{ bug.capturingScreenshot() ? i18n.t('lt.bug.capturing') : buttonText() }}</span>\n }\n </button>\n}\n<p class=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\" data-slot=\"bug-report-announcement\">{{ announcement() }}</p>\n"
|
|
148
|
+
}]
|
|
149
|
+
}],
|
|
150
|
+
ctorParameters: () => [],
|
|
151
|
+
propDecorators: {
|
|
152
|
+
color: [{
|
|
153
|
+
type: i0.Input,
|
|
154
|
+
args: [{
|
|
155
|
+
isSignal: true,
|
|
156
|
+
alias: "color",
|
|
157
|
+
required: false
|
|
158
|
+
}]
|
|
159
|
+
}],
|
|
160
|
+
icon: [{
|
|
161
|
+
type: i0.Input,
|
|
162
|
+
args: [{
|
|
163
|
+
isSignal: true,
|
|
164
|
+
alias: "icon",
|
|
165
|
+
required: false
|
|
166
|
+
}]
|
|
167
|
+
}],
|
|
168
|
+
position: [{
|
|
169
|
+
type: i0.Input,
|
|
170
|
+
args: [{
|
|
171
|
+
isSignal: true,
|
|
172
|
+
alias: "position",
|
|
173
|
+
required: false
|
|
174
|
+
}]
|
|
175
|
+
}],
|
|
176
|
+
show: [{
|
|
177
|
+
type: i0.Input,
|
|
178
|
+
args: [{
|
|
179
|
+
isSignal: true,
|
|
180
|
+
alias: "show",
|
|
181
|
+
required: false
|
|
182
|
+
}]
|
|
183
|
+
}],
|
|
184
|
+
text: [{
|
|
185
|
+
type: i0.Input,
|
|
186
|
+
args: [{
|
|
187
|
+
isSignal: true,
|
|
188
|
+
alias: "text",
|
|
189
|
+
required: false
|
|
190
|
+
}]
|
|
191
|
+
}]
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
export { LT_BUG_DEFAULTS, LT_BUG_DEFAULT_ENDPOINT, LT_BUG_OPTIONS, LT_BUG_SCREENSHOT_LOADER, LT_BUG_UI_ATTRIBUTE, LtBugReport, LtBugReportService, LtBugSubmitError, resolveLtBugOptions };
|
|
195
|
+
|
|
196
|
+
//# sourceMappingURL=lenne.tech-ng-base-bug.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lenne.tech-ng-base-bug.mjs","names":[],"sources":["../../../projects/ng-base/bug/src/lib/ui/bug-report.ts","../../../projects/ng-base/bug/src/lib/ui/bug-report.html"],"sourcesContent":["import { booleanAttribute, Component, computed, effect, inject, Injector, input, signal, untracked } from '@angular/core';\nimport { LT_I18N_OPTIONS, LtI18nService } from '@lenne.tech/ng-base';\nimport { NgIcon, provideIcons } from '@ng-icons/core';\nimport { lucideBug, lucideLoaderCircle } from '@ng-icons/lucide';\n\nimport { LtBugReportService } from '../bug-report-service';\nimport type { LtBugPosition } from '../bug-types';\nimport { registerLtBugEagerMessages } from '../i18n/load';\n\nconst POSITION_CLASSES: Record<LtBugPosition, string> = {\n 'bottom-left': 'bottom-4 left-4',\n 'bottom-right': 'bottom-4 right-4',\n 'top-left': 'top-4 left-4',\n 'top-right': 'top-4 right-4',\n};\n\n/**\n * Floating bug report button (the counterpart of `<BugReportButton>` of `@lenne.tech/bug.lt`). A click takes a\n * screenshot of the current page and opens the report dialog; the dialog (form, attachments, user journey) is loaded\n * on first use, so this component stays small. Creating it starts the capture (`LtBugReportService.start()`), it\n * serves `LtBugReportService.open()` requests, e.g. from `autoOpenOnError`, and renders nothing while the reporter is\n * inactive (production, or without `provideLtBug()`).\n *\n * Inputs override the `provideLtBug()` options for this instance. Render it once in the app shell, deferred and only\n * while the reporter is active (`LT_BUG_ACTIVE` from `@lenne.tech/ng-base/bug/provide`):\n *\n * @example\n * @if (bugActive) { @defer (on immediate) { <lt-bug-report /> } }\n */\n@Component({\n selector: 'lt-bug-report',\n imports: [NgIcon],\n providers: [provideIcons({ lucideBug, lucideLoaderCircle })],\n templateUrl: './bug-report.html',\n host: { 'data-lt-bug-ui': '' },\n})\nexport class LtBugReport {\n /** Button background (default option `buttonColor`). */\n readonly color = input<string>();\n /** ng-icons name of the icon (default option `buttonIcon`, else the Lucide bug); register custom icons yourself. */\n readonly icon = input<string>();\n /** Corner (default option `position`). */\n readonly position = input<LtBugPosition>();\n /** Show the floating button (default option `autoShow`). */\n readonly show = input<boolean | undefined, unknown>(undefined, { transform: (value: unknown) => (value === undefined ? undefined : booleanAttribute(value)) });\n /** Text next to the icon (default option `buttonText`). */\n readonly text = input<string>();\n\n protected readonly bug = inject(LtBugReportService);\n protected readonly i18n = inject(LtI18nService);\n private readonly injector = inject(Injector);\n\n /** Polite announcement after a sent report. */\n protected readonly announcement = signal('');\n protected readonly visible = computed(() => this.bug.active && (this.show() ?? this.bug.options.autoShow));\n protected readonly positionClass = computed(() => POSITION_CLASSES[this.position() ?? this.bug.options.position] ?? POSITION_CLASSES['bottom-right']);\n protected readonly buttonColor = computed(() => this.color() || this.bug.options.buttonColor);\n protected readonly buttonText = computed(() => this.text() ?? this.bug.options.buttonText ?? '');\n protected readonly buttonIcon = computed(() => this.icon() || this.bug.options.buttonIcon || 'lucideBug');\n\n constructor() {\n // The capture starts with the first instance (idempotent; a no-op while inactive and on the server).\n this.bug.start();\n // Only the button texts are needed up front; the dialog chunk registers the rest.\n registerLtBugEagerMessages(this.i18n, inject(LT_I18N_OPTIONS).messages);\n effect(() => {\n if (this.bug.openRequested() && this.bug.active) {\n untracked(() => {\n this.bug.openRequested.set(false);\n void this.openDialog();\n });\n }\n });\n }\n\n /** Take the screenshot, then open the dialog; resolves `true` when a report was sent. */\n async openDialog(): Promise<boolean> {\n if (!this.bug.active || this.bug.isOpen()) {\n return false;\n }\n this.bug.isOpen.set(true);\n this.announcement.set('');\n try {\n // Before the dialog exists, so the screenshot shows the page as the user saw it.\n await this.bug.captureScreenshot();\n const { openLtBugReportDialog } = await import('./bug-report-dialog');\n const sent = (await openLtBugReportDialog(this.injector).closed) === true;\n if (sent) {\n this.announcement.set(this.i18n.t('lt.bug.successTitle'));\n }\n return sent;\n } catch (error) {\n console.error('[@lenne.tech/ng-base/bug] Could not open the bug report dialog', error);\n return false;\n } finally {\n this.bug.isOpen.set(false);\n }\n }\n}\n","@if (visible()) {\n <button\n type=\"button\"\n class=\"fixed z-[9999] inline-flex cursor-pointer items-center justify-center gap-2 rounded-full font-medium text-white shadow-lg transition-shadow duration-200 outline-none hover:shadow-xl focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:ring-offset-2 aria-disabled:cursor-wait print:hidden\"\n data-slot=\"bug-report-button\"\n aria-haspopup=\"dialog\"\n [class]=\"positionClass()\"\n [class.size-12]=\"!buttonText()\"\n [class.h-12]=\"!!buttonText()\"\n [class.px-5]=\"!!buttonText()\"\n [style.background-color]=\"buttonColor()\"\n [attr.aria-label]=\"buttonText() ? null : i18n.t('lt.bug.button')\"\n [attr.aria-busy]=\"bug.capturingScreenshot() || null\"\n [attr.aria-disabled]=\"bug.capturingScreenshot() || null\"\n (click)=\"openDialog()\"\n >\n @if (bug.capturingScreenshot()) {\n <ng-icon name=\"lucideLoaderCircle\" class=\"animate-spin text-2xl\" aria-hidden=\"true\" />\n } @else {\n <ng-icon class=\"text-2xl\" aria-hidden=\"true\" [name]=\"buttonIcon()\" />\n }\n @if (buttonText()) {\n <span>{{ bug.capturingScreenshot() ? i18n.t('lt.bug.capturing') : buttonText() }}</span>\n }\n </button>\n}\n<p class=\"sr-only\" aria-live=\"polite\" aria-atomic=\"true\" data-slot=\"bug-report-announcement\">{{ announcement() }}</p>\n"],"mappings":"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"}
|