├── .dockerignore ├── .env ├── .env.local-temporal ├── .env.test ├── .env.test.e2e ├── .env.test.integration ├── .env.testing ├── .env.ui-server ├── .eslintrc.cjs ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── actions │ └── setup-node │ │ └── action.yml ├── pull_request_template.md ├── release-drafter.yml └── workflows │ ├── chromatic.yml │ ├── lint-and-test.yml │ ├── playwright.yml │ ├── release-draft.yml │ ├── release-published.yml │ ├── storybook-tests.yml │ └── test.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.json ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .storybook ├── main.ts ├── preview.ts └── test-runner.ts ├── .stylelintrc ├── .vscode ├── css-custom-data.json ├── extensions.json ├── launch.json ├── settings.json └── temporal-ui.code-snippets ├── .whitesource ├── LICENSE ├── README.md ├── chromatic.config.json ├── package.json ├── playwright.config.ts ├── plugins ├── vite-plugin-temporal-server.ts └── vite-plugin-ui-server.ts ├── pnpm-lock.yaml ├── postcss.config.cjs ├── scripts ├── audit-tailwind-colors │ ├── index.ts │ ├── parse-tailwind-class.ts │ ├── to-rows.ts │ ├── types.ts │ └── utilities.ts ├── download-temporal.ts ├── generate-holocene-props.ts ├── get-project-root.ts ├── start-codec-server.ts ├── start-temporal-server.ts ├── start-ui-server.ts └── workflows.ts ├── server ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cmd │ └── server │ │ ├── main.go │ │ └── main_test.go ├── config │ ├── base.yaml │ ├── development.yaml │ ├── docker.yaml │ └── e2e.yaml ├── docker │ ├── README.md │ └── start-ui-server.sh ├── go.mod ├── go.sum ├── plugins │ └── fs_config_provider │ │ ├── fs_config_provider.go │ │ ├── loader.go │ │ └── loader_test.go ├── proto │ └── dependencies │ │ └── github.com │ │ ├── gogo │ │ ├── googleapis │ │ │ └── google │ │ │ │ ├── api │ │ │ │ ├── annotations.proto │ │ │ │ ├── http.proto │ │ │ │ └── httpbody.proto │ │ │ │ └── rpc │ │ │ │ ├── code.proto │ │ │ │ ├── error_details.proto │ │ │ │ └── status.proto │ │ └── protobuf │ │ │ └── gogoproto │ │ │ ├── gogo.pb.golden │ │ │ └── gogo.proto │ │ └── grpc-ecosystem │ │ └── grpc-gateway │ │ ├── internal │ │ └── descriptor │ │ │ ├── apiconfig │ │ │ └── apiconfig.proto │ │ │ └── openapiconfig │ │ │ └── openapiconfig.proto │ │ └── protoc-gen-openapiv2 │ │ └── options │ │ ├── annotations.proto │ │ └── openapiv2.proto ├── server │ ├── api │ │ ├── handler.go │ │ ├── marshaler.go │ │ ├── middleware.go │ │ ├── rawhistory.go │ │ └── rawhistory_test.go │ ├── auth │ │ ├── auth.go │ │ ├── auth_test.go │ │ └── oidc.go │ ├── config │ │ ├── auth.go │ │ ├── config.go │ │ ├── config_provider.go │ │ └── config_provider_with_refresh.go │ ├── csrf │ │ ├── skipper.go │ │ └── skipper_test.go │ ├── headers │ │ └── headers.go │ ├── route │ │ ├── api.go │ │ ├── auth.go │ │ ├── health.go │ │ ├── public_path.go │ │ └── ui.go │ ├── rpc │ │ ├── rpc.go │ │ ├── tls.go │ │ └── tls_cert_loader_test.go │ ├── server.go │ ├── server_options │ │ ├── apply_func_container.go │ │ ├── server_option.go │ │ └── server_options.go │ └── version │ │ ├── version.go │ │ └── with_version_header.go └── ui │ └── embed.go ├── src ├── app.css ├── app.d.ts ├── app.html ├── env.d.ts ├── fixtures │ ├── all-event-types.json │ ├── cluster.json │ ├── event-groups.canceled.json │ ├── event-groups.completed.json │ ├── event-groups.continued-as-new.json │ ├── event-groups.failed.json │ ├── event-groups.running.json │ ├── event-groups.terminated.json │ ├── event-groups.timed-out.json │ ├── events.canceled.json │ ├── events.children.json │ ├── events.completed.json │ ├── events.continued-as-new.json │ ├── events.failed.json │ ├── events.running.json │ ├── events.terminated.json │ ├── events.timed-out.json │ ├── large-number-of-child-workflows.json │ ├── list-workflows.json │ ├── local-activities │ │ ├── dotnet_local_activity.json │ │ ├── go_local_activity.json │ │ ├── java_local_activity.json │ │ ├── python_local_activity.json │ │ └── ts_local_activity.json │ ├── namespaces.json │ ├── raw-events.ascending.canceled.json │ ├── raw-events.ascending.completed.json │ ├── raw-events.ascending.continued-as-new.json │ ├── raw-events.ascending.failed.json │ ├── raw-events.ascending.running.json │ ├── raw-events.ascending.terminated.json │ ├── raw-events.ascending.timed-out.json │ ├── raw-events.descending.canceled.json │ ├── raw-events.descending.completed.json │ ├── raw-events.descending.continued-as-new.json │ ├── raw-events.descending.failed.json │ ├── raw-events.descending.running.json │ ├── raw-events.descending.terminated.json │ ├── raw-events.descending.timed-out.json │ ├── settings.json │ ├── stacktrace-query.go-sdk.json │ ├── stacktrace-query.go-sdk.windows.json │ ├── stacktrace-query.java-sdk.json │ ├── stacktrace-query.java-sdk.windows.json │ ├── stacktrace-query.ts-sdk.json │ ├── stacktrace-query.ts-sdk.windows.json │ ├── task-queue-rules.json │ ├── workflow.canceled.json │ ├── workflow.completed.json │ ├── workflow.continued-as-new.json │ ├── workflow.failed.json │ ├── workflow.pending-activities.json │ ├── workflow.pending-children.json │ ├── workflow.running.json │ ├── workflow.scheduled.json │ ├── workflow.terminated.json │ └── workflow.timed-out.json ├── global.d.ts ├── hooks.server.ts ├── lib │ ├── components │ │ ├── advanced-visibility-guard.svelte │ │ ├── auto-refresh-workflow.svelte │ │ ├── batch-operations │ │ │ ├── details.svelte │ │ │ ├── header.svelte │ │ │ ├── results.svelte │ │ │ └── table.svelte │ │ ├── bottom-nav-links.svelte │ │ ├── bottom-nav-namespaces.svelte │ │ ├── bottom-nav-settings.svelte │ │ ├── bottom-nav.svelte │ │ ├── codec-endpoint-settings.svelte │ │ ├── codec-server-error-banner.svelte │ │ ├── dark-mode-menu.svelte │ │ ├── data-encoder-settings.svelte │ │ ├── data-encoder-status.svelte │ │ ├── deployments │ │ │ ├── deployment-status.svelte │ │ │ ├── deployment-table-row.svelte │ │ │ ├── version-details.svelte │ │ │ └── version-table-row.svelte │ │ ├── detail-list │ │ │ ├── detail-list-column.svelte │ │ │ ├── detail-list-label.svelte │ │ │ ├── detail-list-link-value.svelte │ │ │ ├── detail-list-text-value.svelte │ │ │ ├── detail-list-value.svelte │ │ │ ├── detail-list.svelte │ │ │ └── index.ts │ │ ├── event │ │ │ ├── event-category-multiselect-filter.svelte │ │ │ ├── event-date-filter.svelte │ │ │ ├── event-details-full.svelte │ │ │ ├── event-details-link.svelte │ │ │ ├── event-details-row-expanded.svelte │ │ │ ├── event-details-row.svelte │ │ │ ├── event-empty-row.svelte │ │ │ ├── event-group-details.svelte │ │ │ ├── event-link.svelte │ │ │ ├── event-links-expanded.svelte │ │ │ ├── event-metadata-expanded.svelte │ │ │ ├── event-shortcut-keys.svelte │ │ │ ├── event-summary-row.svelte │ │ │ ├── event-summary-table.svelte │ │ │ ├── event-summary.svelte │ │ │ ├── metadata-decoder.svelte │ │ │ ├── payload-decoder.svelte │ │ │ ├── pending-activity-summary-row.svelte │ │ │ └── pending-nexus-summary-row.svelte │ │ ├── feature-guard.svelte │ │ ├── feedback-button.svelte │ │ ├── heart-beat-indicator.svelte │ │ ├── import │ │ │ └── event-history-file-import.svelte │ │ ├── is-cloud-guard.svelte │ │ ├── is-legacy-cloud-guard.svelte │ │ ├── is-oss-guard.svelte │ │ ├── is-temporal-server-version-guard.svelte │ │ ├── lines-and-dots │ │ │ ├── constants.ts │ │ │ ├── end-time-interval.svelte │ │ │ ├── event-classification-filter.svelte │ │ │ ├── event-sort-filter.svelte │ │ │ ├── event-type-filter.svelte │ │ │ ├── sdk-logo.svelte │ │ │ ├── svg │ │ │ │ ├── box.svelte │ │ │ │ ├── dot.svelte │ │ │ │ ├── graph-widget.svelte │ │ │ │ ├── group-details-row.svelte │ │ │ │ ├── group-details-text.svelte │ │ │ │ ├── history-graph-row-visual.svelte │ │ │ │ ├── history-graph.svelte │ │ │ │ ├── line.svelte │ │ │ │ ├── text-link.svelte │ │ │ │ ├── text.svelte │ │ │ │ ├── timeline-axis.svelte │ │ │ │ ├── timeline-graph-row.svelte │ │ │ │ ├── timeline-graph.svelte │ │ │ │ └── workflow-row.svelte │ │ │ ├── workflow-detail.svelte │ │ │ ├── workflow-details.svelte │ │ │ ├── workflow-error-stack-trace.svelte │ │ │ ├── workflow-error.svelte │ │ │ └── workflow-pending-task.svelte │ │ ├── login-button.svelte │ │ ├── namespace-picker.svelte │ │ ├── nexus-guard.svelte │ │ ├── page-title.svelte │ │ ├── panel.svelte │ │ ├── payload-input-with-encoding.svelte │ │ ├── payload-input.svelte │ │ ├── poller-icon.svelte │ │ ├── schedule │ │ │ ├── schedule-advanced-settings.svelte │ │ │ ├── schedule-basic-frequency.svelte │ │ │ ├── schedule-day-of-month-view.svelte │ │ │ ├── schedule-day-of-week-view.svelte │ │ │ ├── schedule-error.svelte │ │ │ ├── schedule-form-view.svelte │ │ │ ├── schedule-frequency-panel.svelte │ │ │ ├── schedule-frequency.svelte │ │ │ ├── schedule-input-payload.svelte │ │ │ ├── schedule-input.svelte │ │ │ ├── schedule-notes.svelte │ │ │ ├── schedule-recent-runs.svelte │ │ │ ├── schedule-search-attributes.svelte │ │ │ ├── schedule-upcoming-runs.svelte │ │ │ ├── schedules-calendar-view.svelte │ │ │ ├── schedules-count.svelte │ │ │ ├── schedules-interval-view.svelte │ │ │ ├── schedules-search-attributes-inputs.svelte │ │ │ ├── schedules-table-row.svelte │ │ │ └── schedules-time-view.svelte │ │ ├── search-attribute-filter │ │ │ ├── boolean-filter.svelte │ │ │ ├── close-filter-button.svelte │ │ │ ├── conditional-menu.svelte │ │ │ ├── datetime-filter.svelte │ │ │ ├── duration-filter.svelte │ │ │ ├── filter-list.svelte │ │ │ ├── index.svelte │ │ │ ├── list-filter.svelte │ │ │ ├── number-filter.svelte │ │ │ ├── search-attribute-menu.svelte │ │ │ ├── status-filter.svelte │ │ │ └── text-filter.svelte │ │ ├── search.stories.svelte │ │ ├── search.svelte │ │ ├── side-nav.svelte │ │ ├── skip-nav.svelte │ │ ├── timezone-select.svelte │ │ ├── top-nav.svelte │ │ ├── worker-compatibility.svelte │ │ ├── worker-rules.svelte │ │ ├── worker-table.svelte │ │ ├── workflow-actions.svelte │ │ ├── workflow-raw-history-link.svelte │ │ ├── workflow-status.stories.svelte │ │ ├── workflow-status.svelte │ │ ├── workflow-versioning-header.svelte │ │ └── workflow │ │ │ ├── add-search-attributes.svelte │ │ │ ├── child-workflows-table.svelte │ │ │ ├── client-actions │ │ │ ├── batch-cancel-confirmation-modal.svelte │ │ │ ├── batch-operation-confirmation-form.svelte │ │ │ ├── batch-reset-confirmation-modal.svelte │ │ │ ├── batch-terminate-confirmation-modal.svelte │ │ │ ├── cancel-confirmation-modal.svelte │ │ │ ├── reset-confirmation-modal.svelte │ │ │ ├── signal-confirmation-modal.svelte │ │ │ ├── terminate-confirmation-modal.svelte │ │ │ └── update-confirmation-modal.svelte │ │ │ ├── configurable-table-headers-drawer │ │ │ ├── index.svelte │ │ │ └── orderable-list.svelte │ │ │ ├── download-event-history-modal.svelte │ │ │ ├── dropdown-filter │ │ │ ├── conditional.svelte │ │ │ ├── datetime-inputs.svelte │ │ │ ├── text-filter.svelte │ │ │ ├── workflow-datetime-filter.svelte │ │ │ └── workflow-status.svelte │ │ │ ├── first-previous-next-workflow-table.svelte │ │ │ ├── input-and-results-payload.svelte │ │ │ ├── input-and-results.svelte │ │ │ ├── live-child-workflows-table.svelte │ │ │ ├── metadata │ │ │ ├── workflow-current-details.svelte │ │ │ └── workflow-summary-and-details.svelte │ │ │ ├── parent-workflow-table.svelte │ │ │ ├── pending-activities.svelte │ │ │ ├── pending-activity │ │ │ └── workflow-pending-activity.svelte │ │ │ ├── relationships │ │ │ ├── workflow-family-node-description-details.svelte │ │ │ ├── workflow-family-node-description-tree.svelte │ │ │ ├── workflow-family-node-description.svelte │ │ │ ├── workflow-family-node-tree.svelte │ │ │ └── workflow-family-tree.svelte │ │ │ ├── scheduler-table.svelte │ │ │ ├── search-attribute-filter │ │ │ └── index.svelte │ │ │ ├── search-attribute-input │ │ │ ├── datetime-input.svelte │ │ │ └── index.svelte │ │ │ ├── start-workflow-button.svelte │ │ │ ├── workflow-advanced-search.svelte │ │ │ ├── workflow-call-stack-error.svelte │ │ │ ├── workflow-callback.svelte │ │ │ ├── workflow-callbacks.svelte │ │ │ ├── workflow-compatibility-error.svelte │ │ │ ├── workflow-count-refresh.svelte │ │ │ ├── workflow-counts.svelte │ │ │ ├── workflow-detail.svelte │ │ │ ├── workflow-error.svelte │ │ │ ├── workflow-filters.svelte │ │ │ ├── workflow-json-navigator.svelte │ │ │ ├── workflow-loading-row.svelte │ │ │ ├── workflow-relationships-old.svelte │ │ │ ├── workflow-relationships.svelte │ │ │ ├── workflow-summary.svelte │ │ │ ├── workflows-summary-configurable-table.svelte │ │ │ ├── workflows-summary-configurable-table │ │ │ ├── batch-actions.svelte │ │ │ ├── filterable-table-cell.svelte │ │ │ ├── table-body-cell.svelte │ │ │ ├── table-empty-state.svelte │ │ │ ├── table-header-cell.svelte │ │ │ ├── table-header-row.svelte │ │ │ ├── table-row.svelte │ │ │ └── table.svelte │ │ │ ├── workflows-summary-row.svelte │ │ │ └── workflows-summary-table.svelte │ ├── holocene │ │ ├── README.md │ │ ├── accordion │ │ │ ├── accordion-group.svelte │ │ │ ├── accordion-light.svelte │ │ │ ├── accordion.stories.svelte │ │ │ └── accordion.svelte │ │ ├── alert.stories.svelte │ │ ├── alert.svelte │ │ ├── api-pagination.svelte │ │ ├── badge.stories.svelte │ │ ├── badge.svelte │ │ ├── banner │ │ │ └── banner.svelte │ │ ├── button.stories.svelte │ │ ├── button.svelte │ │ ├── calendar.svelte │ │ ├── card.stories.svelte │ │ ├── card.svelte │ │ ├── checkbox.stories.svelte │ │ ├── checkbox.svelte │ │ ├── chip.stories.svelte │ │ ├── chip.svelte │ │ ├── code-block.stories.svelte │ │ ├── code-block.svelte │ │ ├── collapsible-divider │ │ │ ├── collapsible-divider.stories.svelte │ │ │ └── collapsible-divider.svelte │ │ ├── combobox │ │ │ ├── async-test.svelte │ │ │ ├── combobox-option.svelte │ │ │ ├── combobox.stories.svelte │ │ │ └── combobox.svelte │ │ ├── compatibility-badge.stories.svelte │ │ ├── compatibility-badge.svelte │ │ ├── copyable │ │ │ ├── button.svelte │ │ │ └── index.svelte │ │ ├── date-picker.stories.svelte │ │ ├── date-picker.svelte │ │ ├── day-of-month-picker.svelte │ │ ├── day-of-week-picker.svelte │ │ ├── drawer-content.svelte │ │ ├── drawer.stories.svelte │ │ ├── drawer.svelte │ │ ├── empty-state.stories.svelte │ │ ├── empty-state.svelte │ │ ├── error-boundary.svelte │ │ ├── error.svelte │ │ ├── expandable │ │ │ └── button.svelte │ │ ├── feature-tag.svelte │ │ ├── file-input.stories.svelte │ │ ├── file-input.svelte │ │ ├── filter-or-copy-buttons.svelte │ │ ├── holocene-components.json │ │ ├── icon-button.stories.svelte │ │ ├── icon-button.svelte │ │ ├── icon │ │ │ ├── __snapshots__ │ │ │ │ └── icon.test.ts.snap │ │ │ ├── icon.stories.svelte │ │ │ ├── icon.story.md │ │ │ ├── icon.svelte │ │ │ ├── index.ts │ │ │ ├── paths.ts │ │ │ ├── svg.svelte │ │ │ └── svg │ │ │ │ ├── activity.svelte │ │ │ │ ├── add.svelte │ │ │ │ ├── apple.svelte │ │ │ │ ├── archives.svelte │ │ │ │ ├── arrow-down.svelte │ │ │ │ ├── arrow-left.svelte │ │ │ │ ├── arrow-right.svelte │ │ │ │ ├── arrow-up.svelte │ │ │ │ ├── ascending.svelte │ │ │ │ ├── astronaut.svelte │ │ │ │ ├── batch-operation.svelte │ │ │ │ ├── book-sparkles.svelte │ │ │ │ ├── book.svelte │ │ │ │ ├── bookmark.svelte │ │ │ │ ├── calendar-plus.svelte │ │ │ │ ├── calendar.svelte │ │ │ │ ├── canceled.svelte │ │ │ │ ├── chart-ascending.svelte │ │ │ │ ├── chart.svelte │ │ │ │ ├── checkmark.svelte │ │ │ │ ├── chevron-down.svelte │ │ │ │ ├── chevron-left.svelte │ │ │ │ ├── chevron-right.svelte │ │ │ │ ├── chevron-selector-vertical.svelte │ │ │ │ ├── chevron-up.svelte │ │ │ │ ├── circle-check-filled.svelte │ │ │ │ ├── circle-check.svelte │ │ │ │ ├── circle-question.svelte │ │ │ │ ├── clock.svelte │ │ │ │ ├── close.svelte │ │ │ │ ├── comet-solid.svelte │ │ │ │ ├── comet.svelte │ │ │ │ ├── compact.svelte │ │ │ │ ├── connection.svelte │ │ │ │ ├── converter-down.svelte │ │ │ │ ├── converter-up.svelte │ │ │ │ ├── copy.svelte │ │ │ │ ├── credit-card.svelte │ │ │ │ ├── descending.svelte │ │ │ │ ├── dollar-badge.svelte │ │ │ │ ├── download.svelte │ │ │ │ ├── drained.svelte │ │ │ │ ├── error.svelte │ │ │ │ ├── exit.svelte │ │ │ │ ├── expand.svelte │ │ │ │ ├── external-link.svelte │ │ │ │ ├── eye-hide.svelte │ │ │ │ ├── eye-show.svelte │ │ │ │ ├── feather.svelte │ │ │ │ ├── feed.svelte │ │ │ │ ├── feedback-circle.svelte │ │ │ │ ├── feedback.svelte │ │ │ │ ├── file-import.svelte │ │ │ │ ├── file-upload.svelte │ │ │ │ ├── filter-solid.svelte │ │ │ │ ├── filter.svelte │ │ │ │ ├── fire-extinguisher.svelte │ │ │ │ ├── flag.svelte │ │ │ │ ├── github.svelte │ │ │ │ ├── graph.svelte │ │ │ │ ├── heartbeat.svelte │ │ │ │ ├── hyphen.svelte │ │ │ │ ├── import.svelte │ │ │ │ ├── inactive.svelte │ │ │ │ ├── info.svelte │ │ │ │ ├── json.svelte │ │ │ │ ├── keyboard.svelte │ │ │ │ ├── labs.svelte │ │ │ │ ├── lightning-bolt.svelte │ │ │ │ ├── link.svelte │ │ │ │ ├── linux.svelte │ │ │ │ ├── lock.svelte │ │ │ │ ├── logout.svelte │ │ │ │ ├── marker.svelte │ │ │ │ ├── merge.svelte │ │ │ │ ├── microsoft.svelte │ │ │ │ ├── minimize.svelte │ │ │ │ ├── moon.svelte │ │ │ │ ├── namespace-switcher.svelte │ │ │ │ ├── namespace.svelte │ │ │ │ ├── nexus.svelte │ │ │ │ ├── office-buildings.svelte │ │ │ │ ├── pause.svelte │ │ │ │ ├── pin-filled.svelte │ │ │ │ ├── pin.svelte │ │ │ │ ├── play.svelte │ │ │ │ ├── regions.svelte │ │ │ │ ├── relationship.svelte │ │ │ │ ├── retention.svelte │ │ │ │ ├── retry.svelte │ │ │ │ ├── robot.svelte │ │ │ │ ├── rocket-ship.svelte │ │ │ │ ├── schedules.svelte │ │ │ │ ├── search.svelte │ │ │ │ ├── settings.svelte │ │ │ │ ├── signal.svelte │ │ │ │ ├── sliders.svelte │ │ │ │ ├── spinner-solid.svelte │ │ │ │ ├── spinner.svelte │ │ │ │ ├── star-empty.svelte │ │ │ │ ├── star-filled.svelte │ │ │ │ ├── summary.svelte │ │ │ │ ├── sun.svelte │ │ │ │ ├── support.svelte │ │ │ │ ├── system-window.svelte │ │ │ │ ├── table.svelte │ │ │ │ ├── target.svelte │ │ │ │ ├── temporal-logo.svelte │ │ │ │ ├── terminal.svelte │ │ │ │ ├── timeline.svelte │ │ │ │ ├── toolbox.svelte │ │ │ │ ├── transcoder-error.svelte │ │ │ │ ├── transcoder-off.svelte │ │ │ │ ├── transcoder-on.svelte │ │ │ │ ├── trash.svelte │ │ │ │ ├── trending-down.svelte │ │ │ │ ├── trending-up.svelte │ │ │ │ ├── tutorial.svelte │ │ │ │ ├── update.svelte │ │ │ │ ├── upload.svelte │ │ │ │ ├── usage.svelte │ │ │ │ ├── vertical-ellipsis.svelte │ │ │ │ ├── warning.svelte │ │ │ │ ├── workflow.svelte │ │ │ │ └── xmark-filled.svelte │ │ ├── input │ │ │ ├── chip-input.stories.svelte │ │ │ ├── chip-input.svelte │ │ │ ├── input.stories.svelte │ │ │ ├── input.svelte │ │ │ ├── number-input.stories.svelte │ │ │ ├── number-input.svelte │ │ │ ├── range-input.stories.svelte │ │ │ └── range-input.svelte │ │ ├── keyboard-shortcut │ │ │ ├── arrow-down.svelte │ │ │ ├── arrow-left.svelte │ │ │ ├── arrow-right.svelte │ │ │ ├── arrow-up.svelte │ │ │ └── shortcut.svelte │ │ ├── label.stories.svelte │ │ ├── label.svelte │ │ ├── labs-mode-guard.svelte │ │ ├── link.stories.svelte │ │ ├── link.svelte │ │ ├── loading.svelte │ │ ├── logo.svelte │ │ ├── main-content-container.svelte │ │ ├── menu │ │ │ ├── index.ts │ │ │ ├── menu-button.svelte │ │ │ ├── menu-container.svelte │ │ │ ├── menu-divider.svelte │ │ │ ├── menu-item.svelte │ │ │ ├── menu.stories.svelte │ │ │ └── menu.svelte │ │ ├── modal.stories.svelte │ │ ├── modal.svelte │ │ ├── monaco │ │ │ ├── editor.svelte │ │ │ ├── index.ts │ │ │ └── markdown.svelte │ │ ├── month-picker.svelte │ │ ├── navigation │ │ │ ├── navigation-badge.svelte │ │ │ ├── navigation-button.svelte │ │ │ ├── navigation-container.svelte │ │ │ └── navigation-item.svelte │ │ ├── orderable-list │ │ │ ├── orderable-list-item.svelte │ │ │ ├── orderable-list.stories.svelte │ │ │ └── orderable-list.svelte │ │ ├── outside-click.ts │ │ ├── page-transition.svelte │ │ ├── pagination.svelte │ │ ├── pill-container │ │ │ ├── pill-container.stories.svelte │ │ │ ├── pill-container.svelte │ │ │ └── pill.svelte │ │ ├── progress-bar.stories.svelte │ │ ├── progress-bar.svelte │ │ ├── radio-input │ │ │ ├── index.ts │ │ │ ├── radio-group.svelte │ │ │ ├── radio-input.stories.svelte │ │ │ ├── radio-input.svelte │ │ │ └── types.ts │ │ ├── scroll-to-bottom.svelte │ │ ├── scroll-to-container.svelte │ │ ├── scroll-to-top.svelte │ │ ├── select │ │ │ ├── filter-select.svelte │ │ │ ├── multi-select.svelte │ │ │ ├── option-group.svelte │ │ │ ├── option.svelte │ │ │ ├── select.stories.svelte │ │ │ ├── select.svelte │ │ │ ├── simple-option.svelte │ │ │ └── simple-select.svelte │ │ ├── skeleton │ │ │ ├── index.svelte │ │ │ ├── skeleton-table.stories.svelte │ │ │ ├── skeleton.stories.svelte │ │ │ ├── table.svelte │ │ │ └── workflow.svelte │ │ ├── split-button.stories.svelte │ │ ├── split-button.svelte │ │ ├── tab-buttons │ │ │ ├── tab-button.stories.svelte │ │ │ ├── tab-button.svelte │ │ │ └── tab-buttons.svelte │ │ ├── tab │ │ │ ├── tab-list.svelte │ │ │ ├── tab-panel.svelte │ │ │ ├── tab.stories.svelte │ │ │ ├── tab.svelte │ │ │ └── tabs.svelte │ │ ├── table │ │ │ ├── paginated-table │ │ │ │ ├── api-paginated.svelte │ │ │ │ ├── index.svelte │ │ │ │ └── paginated.svelte │ │ │ ├── table-header-row.svelte │ │ │ ├── table-row.svelte │ │ │ ├── table.stories.svelte │ │ │ ├── table.story.md │ │ │ └── table.svelte │ │ ├── test-utilities.ts │ │ ├── textarea.stories.svelte │ │ ├── textarea.svelte │ │ ├── time-picker.svelte │ │ ├── toast.svelte │ │ ├── toaster.stories.svelte │ │ ├── toaster.svelte │ │ ├── toggle-button │ │ │ ├── toggle-button.stories.svelte │ │ │ ├── toggle-button.svelte │ │ │ └── toggle-buttons.svelte │ │ ├── toggle-switch.stories.svelte │ │ ├── toggle-switch.svelte │ │ ├── tooltip.stories.svelte │ │ ├── tooltip.svelte │ │ ├── user-menu-mobile.svelte │ │ ├── user-menu.svelte │ │ └── zoom-svg.svelte │ ├── i18n │ │ ├── index.ts │ │ ├── locales │ │ │ ├── en │ │ │ │ ├── batch.ts │ │ │ │ ├── common.ts │ │ │ │ ├── data-encoder.ts │ │ │ │ ├── date-picker.ts │ │ │ │ ├── deployments.ts │ │ │ │ ├── events.ts │ │ │ │ ├── index.ts │ │ │ │ ├── namespaces.ts │ │ │ │ ├── nexus.ts │ │ │ │ ├── schedules.ts │ │ │ │ ├── typed-errors.ts │ │ │ │ ├── workers.ts │ │ │ │ └── workflows.ts │ │ │ └── index.ts │ │ ├── translate.svelte │ │ ├── translate.test.ts │ │ └── translate.ts │ ├── layouts │ │ ├── workflow-header.svelte │ │ ├── workflow-history-layout.svelte │ │ ├── workflow-padded-layout.svelte │ │ └── workflow-run-layout.svelte │ ├── models │ │ ├── __snapshots__ │ │ │ ├── workflow-execution.test.ts.snap │ │ │ └── workflow-execution.write-disabled.test.ts.snap │ │ ├── core-user.ts │ │ ├── event-groups │ │ │ ├── create-event-group.test.ts │ │ │ ├── create-event-group.ts │ │ │ ├── event-groups.d.ts │ │ │ ├── get-event-in-group.test.ts │ │ │ ├── get-event-in-group.ts │ │ │ ├── get-group-for-event.test.ts │ │ │ ├── get-group-for-event.ts │ │ │ ├── get-group-id.test.ts │ │ │ ├── get-group-id.ts │ │ │ ├── get-group-name.ts │ │ │ ├── get-last-event.test.ts │ │ │ ├── get-last-event.ts │ │ │ ├── group-events.test.ts │ │ │ ├── index.ts │ │ │ └── is-event-groups.test.ts │ │ ├── event-history │ │ │ ├── __snapshots__ │ │ │ │ └── get-event-categorization.test.ts.snap │ │ │ ├── get-event-attributes.test.ts │ │ │ ├── get-event-categorization.test.ts │ │ │ ├── get-event-categorization.ts │ │ │ ├── get-event-classification.test.ts │ │ │ ├── get-event-classification.ts │ │ │ ├── index.ts │ │ │ ├── is-event.test.ts │ │ │ ├── simplify-attributes.test.ts │ │ │ ├── simplify-attributes.ts │ │ │ └── to-event-history.test.ts │ │ ├── pending-activities │ │ │ ├── index.ts │ │ │ └── to-decoded-pending-activities.test.ts │ │ ├── search-attribute-filters.ts │ │ ├── workflow-actions.ts │ │ ├── workflow-execution.test.ts │ │ ├── workflow-execution.ts │ │ ├── workflow-execution.write-disabled.test.ts │ │ ├── workflow-filters.ts │ │ └── workflow-status.ts │ ├── pages │ │ ├── batch-operation.svelte │ │ ├── batch-operations.svelte │ │ ├── deployment.svelte │ │ ├── deployments.svelte │ │ ├── import-events-view.svelte │ │ ├── import-events.svelte │ │ ├── nexus-create-endpoint.svelte │ │ ├── nexus-edit-endpoint.svelte │ │ ├── nexus-empty-state.svelte │ │ ├── nexus-endpoint.svelte │ │ ├── nexus-endpoints.svelte │ │ ├── nexus-form.svelte │ │ ├── schedule-edit.svelte │ │ ├── schedule-view.svelte │ │ ├── schedules-create.svelte │ │ ├── schedules.svelte │ │ ├── start-workflow.svelte │ │ ├── task-queue-versioning.svelte │ │ ├── task-queue.svelte │ │ ├── workflow-call-stack.svelte │ │ ├── workflow-history-event.svelte │ │ ├── workflow-history-json.svelte │ │ ├── workflow-history.svelte │ │ ├── workflow-metadata.svelte │ │ ├── workflow-pending-activities.svelte │ │ ├── workflow-query.svelte │ │ ├── workflow-workers.svelte │ │ └── workflows-with-new-search.svelte │ ├── services │ │ ├── batch-service.test.ts │ │ ├── batch-service.ts │ │ ├── cluster-service.ts │ │ ├── data-encoder.test.ts │ │ ├── data-encoder.ts │ │ ├── deployments-service.ts │ │ ├── events-service.ts │ │ ├── namespaces-service.test.ts │ │ ├── namespaces-service.ts │ │ ├── nexus-service.ts │ │ ├── pollers-service.ts │ │ ├── query-service.ts │ │ ├── schedule-service.ts │ │ ├── search-attributes-service.ts │ │ ├── settings-service.test.ts │ │ ├── settings-service.ts │ │ ├── workflow-activities-service.ts │ │ ├── workflow-counts.ts │ │ ├── workflow-service.test.ts │ │ └── workflow-service.ts │ ├── stores │ │ ├── active-events.ts │ │ ├── advanced-visibility.ts │ │ ├── api-pagination.test.ts │ │ ├── api-pagination.ts │ │ ├── auth-user.ts │ │ ├── batch-operations.ts │ │ ├── bulk-actions.test.ts │ │ ├── bulk-actions.ts │ │ ├── capability-enablement.ts │ │ ├── cluster.ts │ │ ├── column-width.ts │ │ ├── configurable-table-columns.test.ts │ │ ├── configurable-table-columns.ts │ │ ├── core-user.ts │ │ ├── data-converter-config.ts │ │ ├── data-encoder-config.ts │ │ ├── data-encoder.test.ts │ │ ├── data-encoder.ts │ │ ├── error.ts │ │ ├── event-view.ts │ │ ├── events.ts │ │ ├── filters.ts │ │ ├── import-events.test.ts │ │ ├── import-events.ts │ │ ├── labs-mode.ts │ │ ├── namespaces.test.ts │ │ ├── namespaces.ts │ │ ├── nav-open.ts │ │ ├── new-feature-tags.ts │ │ ├── pagination.test.ts │ │ ├── pagination.ts │ │ ├── persist-store.ts │ │ ├── persisted-search-parameter.ts │ │ ├── previous-events.ts │ │ ├── reset-workflows.ts │ │ ├── schedules.ts │ │ ├── search-attributes.test.ts │ │ ├── search-attributes.ts │ │ ├── settings.ts │ │ ├── task-queue-view.ts │ │ ├── time-format.test.ts │ │ ├── time-format.ts │ │ ├── toaster.test.ts │ │ ├── toaster.ts │ │ ├── versions.ts │ │ ├── workflow-run.ts │ │ └── workflows.ts │ ├── svelte-mocks │ │ └── app │ │ │ ├── environment.ts │ │ │ ├── navigation.ts │ │ │ ├── paths.ts │ │ │ └── stores.ts │ ├── theme │ │ ├── colors.ts │ │ ├── plugin.ts │ │ ├── preset.ts │ │ ├── types.d.ts │ │ ├── utilities.ts │ │ └── variables.ts │ ├── types │ │ ├── api.ts │ │ ├── batch.ts │ │ ├── deployments.ts │ │ ├── events.ts │ │ ├── global.ts │ │ ├── holocene.ts │ │ ├── index.ts │ │ ├── nexus.ts │ │ ├── schedule.ts │ │ └── workflows.ts │ ├── utilities │ │ ├── advanced-visibility-enabled.test.ts │ │ ├── advanced-visibility-enabled.ts │ │ ├── atob.test.ts │ │ ├── atob.ts │ │ ├── auth-user-cookie.ts │ │ ├── btoa.test.ts │ │ ├── btoa.ts │ │ ├── bulk-actions-enabled.test.ts │ │ ├── bulk-actions-enabled.ts │ │ ├── calendar.ts │ │ ├── cancel-in-progress.test.ts │ │ ├── cancel-in-progress.ts │ │ ├── copy-to-clipboard.ts │ │ ├── dark-mode │ │ │ ├── dark-mode.svelte │ │ │ ├── dark-mode.test.ts │ │ │ ├── dark-mode.ts │ │ │ └── index.ts │ │ ├── decode-payload-test-fixtures.ts │ │ ├── decode-payload.test.ts │ │ ├── decode-payload.ts │ │ ├── encode-payload.test.ts │ │ ├── encode-payload.ts │ │ ├── encode-uri.test.ts │ │ ├── encode-uri.ts │ │ ├── event-formatting.test.ts │ │ ├── event-formatting.ts │ │ ├── event-link-href.test.ts │ │ ├── event-link-href.ts │ │ ├── export-history.ts │ │ ├── export-workflows.ts │ │ ├── focus-trap.test.ts │ │ ├── focus-trap.ts │ │ ├── format-bytes.ts │ │ ├── format-camel-case.test.ts │ │ ├── format-camel-case.ts │ │ ├── format-date.test.ts │ │ ├── format-date.ts │ │ ├── format-event-attributes.test.ts │ │ ├── format-event-attributes.ts │ │ ├── format-time.test.ts │ │ ├── format-time.ts │ │ ├── get-api-origin.test.ts │ │ ├── get-api-origin.ts │ │ ├── get-clusters.test.ts │ │ ├── get-clusters.ts │ │ ├── get-codec.test.ts │ │ ├── get-codec.ts │ │ ├── get-context.ts │ │ ├── get-environment.ts │ │ ├── get-failed-or-pending.test.ts │ │ ├── get-failed-or-pending.ts │ │ ├── get-float-style.test.ts │ │ ├── get-float-style.ts │ │ ├── get-group-status-and-count.ts │ │ ├── get-namespace.test.ts │ │ ├── get-namespace.ts │ │ ├── get-query-types-from-error.test.ts │ │ ├── get-query-types-from-error.ts │ │ ├── get-sdk-version.test.ts │ │ ├── get-sdk-version.ts │ │ ├── get-single-attribute-for-event.test.ts │ │ ├── get-single-attribute-for-event.ts │ │ ├── get-started-completed-and-task-failed-events.test.ts │ │ ├── get-started-completed-and-task-failed-events.ts │ │ ├── get-truncated-word.test.ts │ │ ├── get-truncated-word.ts │ │ ├── get-workflow-relationships.test.ts │ │ ├── get-workflow-relationships.ts │ │ ├── get-workflow-status-filter-code.test.ts │ │ ├── get-workflow-status-filter-code.ts │ │ ├── get-workflow-task-failed-event.test.ts │ │ ├── get-workflow-task-failed-event.ts │ │ ├── handle-error.test.ts │ │ ├── handle-error.ts │ │ ├── has.test.ts │ │ ├── has.ts │ │ ├── is-authorized.test.ts │ │ ├── is-authorized.ts │ │ ├── is-email.ts │ │ ├── is-event-type.test.ts │ │ ├── is-event-type.ts │ │ ├── is-function.test.ts │ │ ├── is-function.ts │ │ ├── is-http.test.ts │ │ ├── is-http.ts │ │ ├── is-network-error.test.ts │ │ ├── is-network-error.ts │ │ ├── is-pending-activity.test.ts │ │ ├── is-pending-activity.ts │ │ ├── is.test.ts │ │ ├── is.ts │ │ ├── merge.test.ts │ │ ├── merge.ts │ │ ├── namespace-url-pattern.test.ts │ │ ├── namespace-url-pattern.ts │ │ ├── nexus-enabled.ts │ │ ├── omit.test.ts │ │ ├── omit.ts │ │ ├── paginated.test.ts │ │ ├── paginated.ts │ │ ├── parse-with-big-int.test.ts │ │ ├── parse-with-big-int.ts │ │ ├── path-matches.test.ts │ │ ├── path-matches.ts │ │ ├── payload-to-string.test.ts │ │ ├── payload-to-string.ts │ │ ├── pending-activities.test.ts │ │ ├── pending-activities.ts │ │ ├── pick.test.ts │ │ ├── pick.ts │ │ ├── pluralize.ts │ │ ├── query │ │ │ ├── filter-workflow-query.test.ts │ │ │ ├── filter-workflow-query.ts │ │ │ ├── is-search-attribute.test.ts │ │ │ ├── is-search-attribute.ts │ │ │ ├── list-workflow-query.test.ts │ │ │ ├── list-workflow-query.ts │ │ │ ├── search-attribute-filter.test.ts │ │ │ ├── search-attribute-filter.ts │ │ │ ├── to-list-workflow-filters.test.ts │ │ │ ├── to-list-workflow-filters.ts │ │ │ ├── to-list-workflow-parameters.test.ts │ │ │ ├── to-list-workflow-parameters.ts │ │ │ ├── tokenize.test.ts │ │ │ └── tokenize.ts │ │ ├── refresh-rate.test.ts │ │ ├── refresh-rate.ts │ │ ├── render-markdown.ts │ │ ├── request-from-api.test.ts │ │ ├── request-from-api.ts │ │ ├── request-from-api.with-access-token.test.ts │ │ ├── route-for-api.test.ts │ │ ├── route-for-api.ts │ │ ├── route-for.test.ts │ │ ├── route-for.ts │ │ ├── schedule-data-formatting.test.ts │ │ ├── schedule-data-formatting.ts │ │ ├── screaming-enums.test.ts │ │ ├── screaming-enums.ts │ │ ├── search-type-parameter.test.ts │ │ ├── search-type-parameter.ts │ │ ├── stack-trace │ │ │ ├── get-file-paths-from-go-stack-trace.test.ts │ │ │ ├── get-file-paths-from-go-stack-trace.ts │ │ │ ├── get-file-paths-from-stack-trace.test.ts │ │ │ ├── get-file-paths-from-stack-trace.ts │ │ │ ├── get-file-paths-from-typescript-stack-trace.test.ts │ │ │ ├── get-file-paths-from-typescript-stack-trace.ts │ │ │ ├── get-sdk-origin.test.ts │ │ │ ├── get-sdk-origin.ts │ │ │ ├── is-from-go-sdk.test.ts │ │ │ ├── is-from-go-sdk.ts │ │ │ ├── is-from-java-sdk.test.ts │ │ │ ├── is-from-java-sdk.ts │ │ │ ├── is-from-typescript-sdk.test.ts │ │ │ └── is-from-typescript-sdk.ts │ │ ├── task-queue-compatibility.test.ts │ │ ├── task-queue-compatibility.ts │ │ ├── to-duration.test.ts │ │ ├── to-duration.ts │ │ ├── to-time-difference.test.ts │ │ ├── to-time-difference.ts │ │ ├── to-url.test.ts │ │ ├── to-url.ts │ │ ├── trim-trailing-slash.test.ts │ │ ├── trim-trailing-slash.ts │ │ ├── unique.test.ts │ │ ├── unique.ts │ │ ├── update-query-parameters.test.ts │ │ ├── update-query-parameters.ts │ │ ├── version-check.test.ts │ │ ├── version-check.ts │ │ ├── workflow-actions.test.ts │ │ ├── workflow-actions.ts │ │ ├── workflow-cancel-enabled.test.ts │ │ ├── workflow-cancel-enabled.ts │ │ ├── workflow-create-disabled.ts │ │ ├── workflow-reset-enabled.test.ts │ │ ├── workflow-reset-enabled.ts │ │ ├── workflow-signal-enabled.test.ts │ │ ├── workflow-signal-enabled.ts │ │ ├── workflow-terminate-enabled.test.ts │ │ ├── workflow-terminate-enabled.ts │ │ ├── workflow-update-enabled.test.ts │ │ ├── workflow-update-enabled.ts │ │ ├── write-actions-are-allowed.test.ts │ │ └── write-actions-are-allowed.ts │ └── vendor │ │ ├── Temporal_Logo_Animation.gif │ │ ├── Temporal_Logo_Animation.mp4 │ │ ├── Temporal_Logo_Animation.webm │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── andromeda.png │ │ ├── apple-touch-icon.png │ │ ├── banner.png │ │ ├── codemirror │ │ └── theme.ts │ │ ├── css │ │ └── normalize.css │ │ ├── empty-state-dark_2x.png │ │ ├── empty-state-dark_4x.png │ │ ├── empty-state-light_2x.png │ │ ├── empty-state-light_4x.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon.ico │ │ ├── logo-dark.svg │ │ ├── logo.svg │ │ ├── sdk-logos │ │ ├── dot-net-logo.png │ │ ├── go-logo.png │ │ ├── java-logo.png │ │ ├── php-logo.png │ │ ├── python-logo.png │ │ ├── ruby-logo.png │ │ ├── rust-logo.png │ │ └── ts-logo.png │ │ └── site.webmanifest ├── markdown.reset.css └── routes │ ├── (app) │ ├── +layout.svelte │ ├── +layout.ts │ ├── +page.svelte │ ├── +page.ts │ ├── import │ │ ├── +layout.svelte │ │ ├── +page.ts │ │ └── events │ │ │ ├── +page.svelte │ │ │ └── [namespace] │ │ │ └── [workflow] │ │ │ └── [run] │ │ │ └── history │ │ │ ├── +layout.svelte │ │ │ ├── +page.ts │ │ │ ├── compact │ │ │ └── +page.svelte │ │ │ ├── feed │ │ │ └── +page.svelte │ │ │ └── json │ │ │ └── +page.svelte │ ├── namespaces │ │ ├── +page.svelte │ │ └── [namespace] │ │ │ ├── +layout.ts │ │ │ ├── +page.svelte │ │ │ ├── +page.ts │ │ │ ├── archival │ │ │ ├── +page.svelte │ │ │ ├── +page.ts │ │ │ ├── _filter-input.svelte │ │ │ └── _workflow-filters.svelte │ │ │ ├── batch-operations │ │ │ ├── +page.svelte │ │ │ └── [jobId] │ │ │ │ └── +page.svelte │ │ │ ├── schedules │ │ │ ├── +page.svelte │ │ │ ├── [schedule] │ │ │ │ ├── +page.svelte │ │ │ │ └── edit │ │ │ │ │ └── +page.svelte │ │ │ └── create │ │ │ │ └── +page.svelte │ │ │ ├── task-queues │ │ │ ├── +layout.svelte │ │ │ ├── +page.svelte │ │ │ └── [queue] │ │ │ │ └── +page.svelte │ │ │ ├── worker-deployments │ │ │ ├── +page.svelte │ │ │ └── [deployment] │ │ │ │ ├── +layout.svelte │ │ │ │ └── +page.svelte │ │ │ └── workflows │ │ │ ├── +page.svelte │ │ │ ├── +page.ts │ │ │ ├── [workflow] │ │ │ ├── +page.ts │ │ │ └── [run] │ │ │ │ ├── +layout.svelte │ │ │ │ ├── +page.svelte │ │ │ │ ├── +page.ts │ │ │ │ ├── call-stack │ │ │ │ └── +page.svelte │ │ │ │ ├── history.json │ │ │ │ └── +page.ts │ │ │ │ ├── history │ │ │ │ ├── +page.svelte │ │ │ │ ├── compact │ │ │ │ │ └── +page.ts │ │ │ │ ├── events │ │ │ │ │ └── [id] │ │ │ │ │ │ └── +page.svelte │ │ │ │ ├── feed │ │ │ │ │ └── +page.ts │ │ │ │ └── json │ │ │ │ │ └── +page.ts │ │ │ │ ├── metadata │ │ │ │ └── +page.svelte │ │ │ │ ├── pending-activities │ │ │ │ └── +page.svelte │ │ │ │ ├── query │ │ │ │ └── +page.svelte │ │ │ │ ├── relationships │ │ │ │ └── +page.svelte │ │ │ │ ├── stack-trace │ │ │ │ └── +page.ts │ │ │ │ └── workers │ │ │ │ └── +page.svelte │ │ │ └── start-workflow │ │ │ ├── +page.svelte │ │ │ └── +page.ts │ ├── nexus │ │ ├── +layout.svelte │ │ ├── +page.svelte │ │ ├── +page.ts │ │ ├── [id] │ │ │ ├── +layout.svelte │ │ │ ├── +layout.ts │ │ │ ├── +page.svelte │ │ │ └── edit │ │ │ │ └── +page.svelte │ │ └── create │ │ │ └── +page.svelte │ ├── render │ │ └── +server.ts │ └── select-namespace │ │ └── +page.svelte │ ├── (login) │ ├── +layout.svelte │ ├── +layout.ts │ ├── login │ │ └── +page.svelte │ └── signin │ │ └── +page.svelte │ ├── +error.svelte │ └── +layout.ts ├── static └── favicon.ico ├── svelte.config.js ├── tailwind.config.ts ├── temporal ├── activities │ ├── double.ts │ ├── echo.ts │ └── index.ts ├── client.ts ├── codec-server.ts ├── data-converter.ts ├── payload-codec.ts ├── workers.ts └── workflows.ts ├── tests ├── accessibility │ ├── empty-states.accessibility.spec.ts │ ├── network-requests │ │ ├── empty-states.har │ │ ├── with-schedules.har │ │ └── with-workflows.har │ ├── with-schedules.accessibility.spec.ts │ └── with-workflows.accessibility.spec.ts ├── e2e │ ├── call-stack.spec.ts │ ├── schedules.spec.ts │ ├── storageState.json │ ├── workflows-summary-table-configuration.spec.ts │ └── workflows.spec.ts ├── fixtures │ └── completed-event-history.json ├── global-setup.ts ├── global-teardown.ts ├── integration │ ├── archival-namespace.spec.ts │ ├── dark-mode.desktop.spec.ts │ ├── dark-mode.mobile.spec.ts │ ├── data-encoder.desktop.spec.ts │ ├── data-encoder.mobile.spec.ts │ ├── disable-write-actions.spec.ts │ ├── import-event-history.spec.ts │ ├── schedule-edit.spec.ts │ ├── schedules-create.spec.ts │ ├── schedules-list.spec.ts │ ├── start-a-workflow.spec.ts │ ├── storageState.json │ ├── workflow-actions.spec.ts │ ├── workflow-bulk-actions.spec.ts │ ├── workflow-event-history.spec.ts │ ├── workflow-navigation.desktop.spec.ts │ ├── workflow-navigation.mobile.spec.ts │ ├── workflow-reset.spec.ts │ ├── workflows-count.spec.ts │ ├── workflows-search-attribute-filter.desktop.spec.ts │ └── workflows-search-attribute-filter.mobile.spec.ts ├── test-utilities │ ├── accessibility-reporter │ │ ├── index.ts │ │ ├── summarize.ts │ │ ├── to-markdown.ts │ │ └── unique.ts │ ├── attach-violations.ts │ ├── custom-matchers.ts │ ├── mock-apis.ts │ ├── mock-date.ts │ ├── mock-local-storage.ts │ └── mocks │ │ ├── batch-operations.ts │ │ ├── cluster.ts │ │ ├── event-history.ts │ │ ├── namespace.ts │ │ ├── namespaces.ts │ │ ├── query.ts │ │ ├── schedules.ts │ │ ├── search-attributes.ts │ │ ├── settings.ts │ │ ├── system-info.ts │ │ ├── task-queues.ts │ │ ├── workflow.ts │ │ ├── workflows-count.ts │ │ └── workflows.ts └── tsconfig.json ├── tsconfig.json ├── utilities ├── temporal-server.ts └── ui-server.ts ├── vercel.json ├── vite.config.ts ├── vitest-setup.ts └── vitest.config.ts /.dockerignore: -------------------------------------------------------------------------------- 1 | .github 2 | .husky 3 | .svelte-kit 4 | .vscode 5 | build 6 | e2e 7 | node_modules 8 | server/ui/assets -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | VITE_TEMPORAL_PORT="7233" 2 | VITE_API="http://localhost:8233" 3 | VITE_MODE="development" 4 | -------------------------------------------------------------------------------- /.env.local-temporal: -------------------------------------------------------------------------------- 1 | VITE_TEMPORAL_PORT="7134" 2 | VITE_API="http://localhost:8081" 3 | VITE_MODE="development" 4 | VITE_TEMPORAL_UI_BUILD_TARGET="local" 5 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | VITE_API="http://localhost:8233" 2 | VITE_MODE="test" 3 | -------------------------------------------------------------------------------- /.env.test.e2e: -------------------------------------------------------------------------------- 1 | VITE_TEMPORAL_UI_BUILD_TARGET="local" 2 | VITE_TEMPORAL_PORT="7233" 3 | VITE_API="http://localhost:8080" 4 | VITE_MODE="development" -------------------------------------------------------------------------------- /.env.test.integration: -------------------------------------------------------------------------------- 1 | VITE_TEMPORAL_PORT="4444" 2 | VITE_API="http://localhost:8233" 3 | VITE_MODE="development" 4 | -------------------------------------------------------------------------------- /.env.testing: -------------------------------------------------------------------------------- 1 | VITE_API="http://localhost:7777" 2 | VITE_TEMPORAL_PORT="6666" 3 | VITE_UI_SERVER_PORT="7777" 4 | VITE_MODE="testing" 5 | -------------------------------------------------------------------------------- /.env.ui-server: -------------------------------------------------------------------------------- 1 | VITE_TEMPORAL_PORT="7233" 2 | VITE_API="http://localhost:8081" 3 | VITE_MODE="development" 4 | VITE_TEMPORAL_UI_BUILD_TARGET="local" 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | ## Is your feature request related to a problem? Please describe. 10 | 11 | 12 | 13 | ## Describe the solution you'd like 14 | 15 | 16 | 17 | ## Describe alternatives you've considered 18 | 19 | 20 | 21 | ## Additional context 22 | 23 | 24 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/release-drafter/release-drafter?tab=readme-ov-file#configuration-options 2 | name-template: 'v$RESOLVED_VERSION 🤖' 3 | tag-template: 'v$RESOLVED_VERSION' 4 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 5 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 6 | version-resolver: 7 | major: 8 | labels: 9 | - 'major' 10 | minor: 11 | labels: 12 | - 'minor' 13 | patch: 14 | labels: 15 | - 'patch' 16 | default: patch 17 | template: | 18 | ## What’s Changed 19 | 20 | $CHANGES 21 | -------------------------------------------------------------------------------- /.github/workflows/chromatic.yml: -------------------------------------------------------------------------------- 1 | name: 'Chromatic' 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request_target: 7 | branches: [main, 'codefreeze-*'] 8 | 9 | jobs: 10 | chromatic: 11 | name: Run Chromatic 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@v4 16 | with: 17 | fetch-depth: 0 18 | - name: Checkout and Setup Node 19 | uses: ./.github/actions/setup-node 20 | - name: Run Chromatic 21 | uses: chromaui/action@latest 22 | with: 23 | projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }} 24 | autoAcceptChanges: 'main' 25 | exitZeroOnChanges: true 26 | exitOnceUploaded: true 27 | onlyChanged: true 28 | skip: 'dependabot/**' 29 | -------------------------------------------------------------------------------- /.github/workflows/storybook-tests.yml: -------------------------------------------------------------------------------- 1 | name: Storybook Tests 2 | on: deployment_status 3 | jobs: 4 | test: 5 | timeout-minutes: 60 6 | runs-on: ubuntu-latest 7 | if: github.event.deployment_status.state == 'success' 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Checkout and Setup Node 11 | uses: ./.github/actions/setup-node 12 | - name: Install Playwright 13 | run: pnpm exec playwright install --with-deps 14 | - name: Run Storybook tests 15 | run: pnpm stories:test 16 | env: 17 | TARGET_URL: '${{ github.event.deployment_status.target_url }}' 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /.svelte-kit 4 | /package 5 | /coverage 6 | *.log 7 | *.swp 8 | /.vercel_build_output 9 | /.vercel 10 | /build-local 11 | /.history 12 | /build 13 | /bin 14 | server/ui-server 15 | server/api 16 | server/ui/assets 17 | /test-results/ 18 | /playwright-report/ 19 | /playwright/.cache/ 20 | /dist 21 | /audits 22 | go.work 23 | go.work.sum 24 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.{ts,js}": ["eslint --fix", "prettier --write"], 3 | "*.{css,postcss}": ["stylelint --fix"], 4 | "*.svelte": ["eslint --fix", "prettier --write", "stylelint --fix"], 5 | "*.{json,md}": "prettier --write" 6 | } 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .svelte-kit/** 2 | build/** 3 | e2e/test-results/** 4 | e2e/playwright-report/** 5 | node_modules/** 6 | coverage/** 7 | server/** 8 | .vercel/** 9 | /package 10 | pnpm-lock.yaml 11 | README.md 12 | playwright-report 13 | dist/**/* 14 | tests/**/storageState.json 15 | *.har 16 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "all", 8 | "bracketSpacing": true, 9 | "bracketSameLine": false, 10 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"] 11 | } 12 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@storybook/sveltekit'; 2 | 3 | const config: StorybookConfig = { 4 | stories: ['../src/**/*.stories.@(ts|svelte)'], 5 | addons: [ 6 | '@storybook/addon-links', 7 | '@storybook/addon-essentials', 8 | '@storybook/addon-interactions', 9 | { 10 | name: '@storybook/addon-svelte-csf', 11 | options: { 12 | // REMOVE WHEN STORIES ARE UPGRADED TO NEW CSF SYNTAX 13 | legacyTemplate: true, 14 | }, 15 | }, 16 | '@storybook/addon-a11y', 17 | '@storybook/addon-themes', 18 | '@chromatic-com/storybook', 19 | ], 20 | framework: '@storybook/sveltekit', 21 | }; 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /.storybook/test-runner.ts: -------------------------------------------------------------------------------- 1 | import type { TestRunnerConfig } from '@storybook/test-runner'; 2 | import { getStoryContext } from '@storybook/test-runner'; 3 | import { injectAxe, checkA11y } from 'axe-playwright'; 4 | 5 | const config: TestRunnerConfig = { 6 | async preVisit(page) { 7 | await injectAxe(page); 8 | }, 9 | async postVisit(page, context) { 10 | const storyContext = await getStoryContext(page, context); 11 | if (storyContext.parameters?.a11y?.disable) { 12 | return; 13 | } 14 | await checkA11y(page, '#storybook-root', { 15 | detailedReport: true, 16 | detailedReportOptions: { 17 | html: true, 18 | }, 19 | }); 20 | }, 21 | }; 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /.vscode/css-custom-data.json: -------------------------------------------------------------------------------- 1 | { 2 | "atDirectives": [ 3 | { 4 | "name": "@tailwind", 5 | "description": "Use the @tailwind directive to insert Tailwind’s `base`, `components`, `utilities`, and `screens` styles into your CSS.", 6 | "references": [ 7 | { 8 | "name": "Tailwind’s “Functions & Directives” documentation", 9 | "url": "https://tailwindcss.com/docs/functions-and-directives/#tailwind" 10 | } 11 | ] 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dbaeumer.vscode-eslint", 4 | "esbenp.prettier-vscode", 5 | "bradlc.vscode-tailwindcss", 6 | "svelte.svelte-vscode", 7 | "yoavbls.pretty-ts-errors" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/node_modules": true, 4 | "**/.svelte-kit": true, 5 | "**/.vercel": true 6 | }, 7 | "editor.formatOnSave": true, 8 | "eslint.validate": ["javascript", "typescript", "svelte"], 9 | "css.customData": [".vscode/css-custom-data.json"], 10 | "typescript.tsdk": "node_modules/typescript/lib", 11 | "tailwindCSS.experimental.classRegex": [ 12 | ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], 13 | ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"] 14 | ], 15 | "files.associations": { 16 | "*.css": "tailwindcss" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/temporal-ui.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Playwright Test": { 3 | "prefix": "pwt", 4 | "body": ["test('${1}', async ({ page }) => {", " $0", "});"], 5 | "description": "useState()", 6 | }, 7 | } 8 | -------------------------------------------------------------------------------- /.whitesource: -------------------------------------------------------------------------------- 1 | { 2 | "settingsInheritedFrom": "temporalio/whitesource-config@main" 3 | } -------------------------------------------------------------------------------- /chromatic.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "autoAcceptChanges": "main", 3 | "buildScriptName": "stories:build", 4 | "exitOnceUploaded": true, 5 | "exitZeroOnChanges": true, 6 | "externals": ["public/**"], 7 | "onlyChanged": true, 8 | "skip": "dependabot/**" 9 | } 10 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | const tailwindcss = require('tailwindcss'); 2 | const tailwindcssNesting = require('tailwindcss/nesting'); 3 | const autoprefixer = require('autoprefixer'); 4 | const cssnano = require('cssnano'); 5 | 6 | const mode = process.env.NODE_ENV; 7 | const dev = mode === 'development'; 8 | 9 | const config = { 10 | plugins: [ 11 | autoprefixer(), 12 | tailwindcssNesting(), 13 | tailwindcss(), 14 | !dev && 15 | cssnano({ 16 | preset: 'default', 17 | }), 18 | ], 19 | }; 20 | 21 | module.exports = config; 22 | -------------------------------------------------------------------------------- /scripts/audit-tailwind-colors/to-rows.ts: -------------------------------------------------------------------------------- 1 | import { type Result } from './types'; 2 | import { getProjectRoot } from '../get-project-root'; 3 | 4 | export const toRow = (result: Result) => { 5 | return [ 6 | result.path.replace(getProjectRoot(), ''), 7 | result.line, 8 | result.class, 9 | result.variant || '', 10 | result.utility || '', 11 | result.color || '', 12 | result.shade || '', 13 | ].join(','); 14 | }; 15 | 16 | export const toRows = (results: Result[]) => { 17 | return results.reduce((rows, result) => { 18 | return rows + toRow(result) + '\n'; 19 | }, 'File,Line Number,Class,Variant,Utility,Color,Shade\n'); 20 | }; 21 | -------------------------------------------------------------------------------- /scripts/audit-tailwind-colors/types.ts: -------------------------------------------------------------------------------- 1 | export type TailwindClass = { 2 | variant: string | null; 3 | utility: string | null; 4 | color: string | null; 5 | shade: string | null; 6 | }; 7 | 8 | export type Result = { 9 | path: string; 10 | line: number; 11 | class: string; 12 | } & TailwindClass; 13 | -------------------------------------------------------------------------------- /scripts/get-project-root.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from 'fs'; 2 | import { dirname, join } from 'path'; 3 | import { fileURLToPath } from 'url'; 4 | 5 | export const getProjectRoot = (): string => { 6 | let currentDirectory = dirname(fileURLToPath(import.meta.url)); 7 | 8 | while (currentDirectory !== '/') { 9 | if (existsSync(join(currentDirectory, 'package.json'))) { 10 | return currentDirectory; 11 | } 12 | currentDirectory = dirname(currentDirectory); 13 | } 14 | 15 | throw new Error('Project root not found.'); 16 | }; 17 | -------------------------------------------------------------------------------- /scripts/start-codec-server.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs/yargs'; 2 | 3 | import { 4 | type CodecServerOptions, 5 | createCodecServer, 6 | } from '../temporal/codec-server'; 7 | 8 | const args = yargs(process.argv.slice(2)).parse(); 9 | 10 | const options: CodecServerOptions = { 11 | port: args['port'], 12 | }; 13 | 14 | const server = await createCodecServer(options); 15 | 16 | server.start().catch(async (error) => { 17 | console.error(error); 18 | await server.stop(); 19 | process.exit(1); 20 | }); 21 | 22 | process.on('beforeExit', () => { 23 | if (server) server.stop(); 24 | }); 25 | -------------------------------------------------------------------------------- /scripts/start-temporal-server.ts: -------------------------------------------------------------------------------- 1 | import yargs from 'yargs/yargs'; 2 | 3 | import { 4 | createTemporalServer, 5 | type TemporalServer, 6 | type TemporalServerOptions, 7 | } from '../utilities/temporal-server'; 8 | 9 | const args = yargs(process.argv.slice(2)).parse(); 10 | 11 | const options: TemporalServerOptions = { 12 | port: args['port'], 13 | uiPort: args['uiPort'] ?? args['ui-port'], 14 | path: args['path'], 15 | logLevel: args['logLevel'] ?? args['log-level'], 16 | codecEndpoint: args['codecEndpoint'] ?? args['codec-endpoint'], 17 | dbFilename: args['dbFilename'] ?? args['db-filename'], 18 | }; 19 | 20 | const server: TemporalServer = await createTemporalServer(options); 21 | 22 | await server.ready(); 23 | 24 | process.on('beforeExit', async () => { 25 | await server.shutdown(); 26 | }); 27 | -------------------------------------------------------------------------------- /scripts/start-ui-server.ts: -------------------------------------------------------------------------------- 1 | import { createUIServer, UIServer } from '../utilities/ui-server'; 2 | 3 | const server: UIServer = await createUIServer(); 4 | 5 | await server.ready(); 6 | 7 | process.on('beforeExit', async () => { 8 | await server.shutdown(); 9 | }); 10 | -------------------------------------------------------------------------------- /scripts/workflows.ts: -------------------------------------------------------------------------------- 1 | import { connect, startWorkflows } from '../temporal/client'; 2 | import { runWorkerUntil } from '../temporal/workers'; 3 | 4 | async function main() { 5 | const client = await connect(); 6 | const results = startWorkflows(client); 7 | await runWorkerUntil(results); 8 | } 9 | 10 | main().catch((error) => { 11 | console.error(error); 12 | process.exit(1); 13 | }); 14 | -------------------------------------------------------------------------------- /server/Makefile: -------------------------------------------------------------------------------- 1 | .ONESHELL: 2 | .PHONY: 3 | 4 | all: build 5 | 6 | ##### Variables ###### 7 | 8 | ifndef GOPATH 9 | GOPATH := $(shell go env GOPATH) 10 | endif 11 | 12 | GOBIN := $(if $(shell go env GOBIN),$(shell go env GOBIN),$(GOPATH)/bin) 13 | PATH := $(GOBIN):$(PATH) 14 | 15 | COLOR := "\e[1;36m%s\e[0m\n" 16 | 17 | ##### Build ##### 18 | build: build-server 19 | 20 | build-server: 21 | go mod tidy 22 | go build -o ui-server ./cmd/server/main.go 23 | 24 | ##### Test ##### 25 | test: clean-test-results 26 | @printf $(COLOR) "Running unit tests..." 27 | go test ./... -race 28 | 29 | clean-test-results: 30 | @rm -f test.log 31 | @go clean -testcache 32 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # ui-server 2 | 3 | [![Publish Docker image](https://github.com/temporalio/ui-server/actions/workflows/docker.yml/badge.svg)](https://github.com/temporalio/ui-server/actions/workflows/docker.yml) 4 | 5 | ## Development 6 | 7 | https://github.com/temporalio/ui-server is automatically updated to mirror 8 | changes to https://github.com/temporalio/ui/tree/main/server; commits should be 9 | made to the UI repository. 10 | 11 | For contributions follow UI's development guide https://github.com/temporalio/ui 12 | 13 | 14 | ## To View gRPC routes: 15 | [Temporal API Workflowservice](https://github.com/temporalio/api/blob/master/temporal/api/workflowservice/v1/service.proto) 16 | -------------------------------------------------------------------------------- /server/config/base.yaml: -------------------------------------------------------------------------------- 1 | temporalGrpcAddress: 127.0.0.1:7233 2 | port: 8233 3 | -------------------------------------------------------------------------------- /server/config/e2e.yaml: -------------------------------------------------------------------------------- 1 | temporalGrpcAddress: 127.0.0.1:7233 2 | port: 8080 3 | codec: 4 | endpoint: http://localhost:8888 5 | -------------------------------------------------------------------------------- /server/docker/start-ui-server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eu -o pipefail 4 | 5 | if [ -f ./config/config-template.yaml ]; then 6 | >&2 echo "Custom config templates should now be mounted at: /home/ui-server/config/docker.yaml" 7 | >&2 echo "Please see the README for details on config templating support" 8 | dockerize -template ./config-template.yaml:./config/docker.yaml 9 | fi 10 | 11 | # Run bash instead of ui-server if "bash" is passed as an argument (convenient to debug docker image). 12 | for arg in "$@" ; do [[ ${arg} == "bash" ]] && bash && exit 0 ; done 13 | 14 | exec ./ui-server --env docker start 15 | -------------------------------------------------------------------------------- /server/ui/embed.go: -------------------------------------------------------------------------------- 1 | package ui 2 | 3 | import ( 4 | "embed" 5 | "io/fs" 6 | ) 7 | 8 | //go:embed all:assets 9 | var assets embed.FS 10 | 11 | func Assets() (fs.FS, error) { 12 | return fs.Sub(assets, "assets") 13 | } 14 | -------------------------------------------------------------------------------- /src/app.css: -------------------------------------------------------------------------------- 1 | @import '@fontsource-variable/inter'; 2 | 3 | @import '@fontsource/noto-sans-mono'; 4 | 5 | @import 'tailwindcss/base'; 6 | @import 'tailwindcss/components'; 7 | @import 'tailwindcss/utilities'; 8 | 9 | @layer base { 10 | *, 11 | html, 12 | body { 13 | @apply box-border; 14 | } 15 | 16 | body { 17 | @apply body-normal surface-background relative overscroll-none; 18 | } 19 | 20 | input[type='search']::-webkit-search-cancel-button { 21 | @apply hidden; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-empty-interface */ 2 | /// 3 | 4 | declare namespace App { 5 | interface Locals {} 6 | 7 | interface Platform {} 8 | 9 | interface Session {} 10 | 11 | interface PageData { 12 | workflow?: import('$types').WorkflowExecution; 13 | settings?: import('$types').Settings; 14 | workers?: import('$types').GetPollersResponse; 15 | cluster?: import('$types').ClusterInformation; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | %sveltekit.head% 7 | 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | type Vitest = import('vitest'); 4 | 5 | interface ImportMetaEnv { 6 | readonly VITE_TEMPORAL_UI_BUILD_TARGET: string; 7 | readonly VITE_API: string; 8 | readonly VITE_DARK_MODE: boolean; 9 | } 10 | 11 | interface ImportMeta { 12 | readonly env: ImportMetaEnv; 13 | readonly vitest: Vitest; 14 | } 15 | -------------------------------------------------------------------------------- /src/fixtures/event-groups.continued-as-new.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/fixtures/event-groups.continued-as-new.json -------------------------------------------------------------------------------- /src/fixtures/raw-events.ascending.continued-as-new.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/fixtures/raw-events.ascending.continued-as-new.json -------------------------------------------------------------------------------- /src/fixtures/raw-events.descending.continued-as-new.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/fixtures/raw-events.descending.continued-as-new.json -------------------------------------------------------------------------------- /src/fixtures/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Auth": { 3 | "Enabled": false, 4 | "Options": null 5 | }, 6 | "BannerText": "", 7 | "DefaultNamespace": "default", 8 | "ShowTemporalSystemNamespace": false, 9 | "FeedbackURL": "", 10 | "NotifyOnNewVersion": true, 11 | "Codec": { 12 | "Endpoint": "", 13 | "PassAccessToken": true 14 | }, 15 | "Version": "2.0.0" 16 | } 17 | -------------------------------------------------------------------------------- /src/fixtures/stacktrace-query.ts-sdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "queryResult": { 3 | "payloads": [ 4 | { 5 | "metadata": { 6 | "encoding": "anNvbi9wbGFpbg==" 7 | }, 8 | "data": " at Promise.then ()\n at global.setTimeout (/Users/user/Desktop/ts-sdk/example/node_modules/@temporalio/workflow/src/worker-interface.ts:85:7)\n at setTimeout (/Users/user/Desktop/ts-sdk/example/src/workflows.ts:10:33)\n at new Promise ()\n at delay (/Users/user/Desktop/ts-sdk/example/src/workflows.ts:10:9)\n at delay (/Users/user/Desktop/ts-sdk/example/src/workflows.ts:16:8)\n\n at delay (/Users/user/Desktop/ts-sdk/example/src/workflows.ts:16:8)" 9 | } 10 | ] 11 | }, 12 | "queryRejected": null 13 | } 14 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '@sveltejs/svelte-virtual-list'; 4 | 5 | declare namespace svelte.JSX { 6 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 | interface HTMLAttributes { 8 | 'onclick-outside': (e: CustomEvent) => void; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/hooks.server.ts: -------------------------------------------------------------------------------- 1 | import type { Handle } from '@sveltejs/kit'; 2 | 3 | export const handle: Handle = async ({ event, resolve }) => { 4 | const response = await resolve(event, {}); 5 | 6 | return response; 7 | }; 8 | -------------------------------------------------------------------------------- /src/lib/components/advanced-visibility-guard.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {#if $supportsAdvancedVisibility} 6 | 7 | {:else} 8 | 9 | {/if} 10 | -------------------------------------------------------------------------------- /src/lib/components/auto-refresh-workflow.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/components/detail-list/detail-list-column.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
14 | {@render children()} 15 |
16 | -------------------------------------------------------------------------------- /src/lib/components/detail-list/detail-list-label.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | {@render children()} 13 |
14 | -------------------------------------------------------------------------------- /src/lib/components/detail-list/detail-list.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
17 | {@render children()} 18 |
19 | -------------------------------------------------------------------------------- /src/lib/components/detail-list/index.ts: -------------------------------------------------------------------------------- 1 | export { default as DetailList } from './detail-list.svelte'; 2 | export { default as DetailListLabel } from './detail-list-label.svelte'; 3 | export { default as DetailListValue } from './detail-list-value.svelte'; 4 | export { default as DetailListTextValue } from './detail-list-text-value.svelte'; 5 | export { default as DetailListLinkValue } from './detail-list-link-value.svelte'; 6 | export { default as DetailListColumn } from './detail-list-column.svelte'; 7 | -------------------------------------------------------------------------------- /src/lib/components/event/event-empty-row.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#if loading} 12 | 13 | {:else} 14 | 15 | {/if} 16 | -------------------------------------------------------------------------------- /src/lib/components/event/event-metadata-expanded.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 |

Summary

13 | 18 | 19 | {decodedValue} 20 | 21 | 22 |
23 | -------------------------------------------------------------------------------- /src/lib/components/feature-guard.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {#if enabled} 6 | 7 | {:else} 8 | 9 | {/if} 10 | -------------------------------------------------------------------------------- /src/lib/components/feedback-button.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 16 | 17 | 18 | 27 | -------------------------------------------------------------------------------- /src/lib/components/is-cloud-guard.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {#if isCloud || $page.data?.settings?.runtimeEnvironment?.isCloud} 8 | 9 | {:else} 10 | 11 | {/if} 12 | -------------------------------------------------------------------------------- /src/lib/components/is-legacy-cloud-guard.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#if isCloud && isLegacyCloud} 12 | 13 | {:else} 14 | 15 | {/if} 16 | -------------------------------------------------------------------------------- /src/lib/components/is-oss-guard.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {#if isCloud || $page.data?.settings?.runtimeEnvironment?.isCloud} 8 | 9 | {:else} 10 | 11 | {/if} 12 | -------------------------------------------------------------------------------- /src/lib/components/is-temporal-server-version-guard.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {#if $isCloud || minimumVersionRequired(minimumVersion, $temporalVersion)} 10 | 11 | {:else} 12 | 13 | {/if} 14 | -------------------------------------------------------------------------------- /src/lib/components/lines-and-dots/svg/text-link.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | goto(href)} 15 | on:keydown={(e) => { 16 | if (e.key === 'Enter') { 17 | goto(href); 18 | } 19 | }} 20 | class="cursor-pointer select-none outline-none" 21 | {x} 22 | {y} 23 | font-size={fontSize} 24 | > 25 | 26 | 27 | 28 | 37 | -------------------------------------------------------------------------------- /src/lib/components/login-button.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 23 | -------------------------------------------------------------------------------- /src/lib/components/nexus-guard.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | {#if nexusEnabled} 8 | 9 | {:else} 10 | 11 | {/if} 12 | -------------------------------------------------------------------------------- /src/lib/components/page-title.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | {title} 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/lib/components/panel.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 7 |
8 | 9 | 18 | -------------------------------------------------------------------------------- /src/lib/components/poller-icon.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 15 | -------------------------------------------------------------------------------- /src/lib/components/schedule/schedule-day-of-week-view.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 |
14 |

15 | {translate('schedules.recurring-days-heading')} 16 |

17 |

18 | {translate('schedules.recurring-days-description')} 19 |

20 | 21 |
22 | 23 |
24 | -------------------------------------------------------------------------------- /src/lib/components/schedule/schedule-error.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 |

{translate('schedules.error-title')}

10 |

11 | {error} 12 |

13 |
14 | 15 | 20 | -------------------------------------------------------------------------------- /src/lib/components/schedule/schedule-frequency-panel.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 |

{translate('schedules.schedule-spec')}

17 |
18 | 19 |
20 |
21 | -------------------------------------------------------------------------------- /src/lib/components/schedule/schedule-input.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/components/schedule/schedule-notes.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 |

{translate('common.notes')}

9 |

{notes}

10 |
11 | -------------------------------------------------------------------------------- /src/lib/components/search-attribute-filter/close-filter-button.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | -------------------------------------------------------------------------------- /src/lib/components/search.stories.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/lib/components/skip-nav.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | {translate('common.skip-nav')} 12 | 13 | -------------------------------------------------------------------------------- /src/lib/components/workflow-versioning-header.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |

13 | {translate('workers.buildId')} 17 |

18 | -------------------------------------------------------------------------------- /src/lib/components/workflow/dropdown-filter/conditional.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 | 20 | -------------------------------------------------------------------------------- /src/lib/components/workflow/workflows-summary-configurable-table/table-header-cell.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | {label} 11 | 12 | -------------------------------------------------------------------------------- /src/lib/components/workflow/workflows-summary-table.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
Workflows
StatusWorkflow IDTypeStartEnd Summary
20 | -------------------------------------------------------------------------------- /src/lib/holocene/README.md: -------------------------------------------------------------------------------- 1 | # Holocene 2 | 3 | Temporal's UI Component Library 4 | 5 | Run `pnpm stories:dev` to open Storybook 6 | -------------------------------------------------------------------------------- /src/lib/holocene/accordion/accordion-group.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/lib/holocene/card.stories.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | 16 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/lib/holocene/card.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | {@render children?.()} 13 |
14 | -------------------------------------------------------------------------------- /src/lib/holocene/error-boundary.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | {#snippet failed(error, reset)} 8 | 9 | {/snippet} 10 | 11 | -------------------------------------------------------------------------------- /src/lib/holocene/feature-tag.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#if hide} 11 | 12 | {:else} 13 | 14 | {/if} 15 | 16 | 25 | -------------------------------------------------------------------------------- /src/lib/holocene/file-input.stories.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/icon.story.md: -------------------------------------------------------------------------------- 1 | # Icon 2 | 3 | The `` component allows for rendering of SVG Icons in our app. It is designed to support SVG's with 24x24 viewboxes only! To add a new icon, follow these steps: 4 | 5 | 1. Obtain the SVG markup from a designer. Make sure it is 24x24px! 6 | 2. Create a new Svelte component in `src/lib/holocene/icon/svg` with the following contents. _Note: It's important to spread `$$props` here!_ 7 | 8 | ``` 9 | 13 | 14 | 15 | ``` 16 | 17 | 3. Take the `` element(s) from your SVG markup, and paste them in the default slot of the `` Component. 18 | 4. Add your new icon to the `icons` object in `src/lib/holocene/icon/paths.ts`. 19 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/icon.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#if icon} 13 | 21 | {/if} 22 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/index.ts: -------------------------------------------------------------------------------- 1 | import Icon from './icon.svelte'; 2 | 3 | export { type IconName, iconNames } from './paths'; 4 | 5 | export default Icon; 6 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | {title} 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/activity.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/add.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/archives.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/arrow-down.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/arrow-left.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/arrow-right.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/arrow-up.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/ascending.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/book-sparkles.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/bookmark.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/calendar-plus.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/calendar.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/chart-ascending.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/chart.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/checkmark.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/chevron-down.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/chevron-left.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/chevron-right.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/chevron-selector-vertical.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/chevron-up.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/close.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/connection.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/converter-down.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/converter-up.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/copy.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/credit-card.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/descending.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/exit.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/expand.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/external-link.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/feed.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/file-import.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/filter-solid.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/filter.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/fire-extinguisher.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/flag.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/graph.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/heartbeat.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/hyphen.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/import.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/json.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/keyboard.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/labs.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/lightning-bolt.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/lock.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/logout.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/merge.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/minimize.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/namespace.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/nexus.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/office-buildings.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/pause.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/play.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/relationship.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/retry.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/schedules.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/sliders.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/spinner-solid.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 14 | 15 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/star-filled.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/summary.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/table.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/terminal.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/timeline.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/toolbox.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/transcoder-off.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/transcoder-on.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 15 | 16 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/trash.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/trending-down.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 12 | 20 | 29 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/trending-up.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 12 | 20 | 29 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/update.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/vertical-ellipsis.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/warning.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /src/lib/holocene/icon/svg/xmark-filled.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 13 | 14 | -------------------------------------------------------------------------------- /src/lib/holocene/keyboard-shortcut/arrow-down.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {title} 15 | -------------------------------------------------------------------------------- /src/lib/holocene/keyboard-shortcut/arrow-left.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {title} 15 | -------------------------------------------------------------------------------- /src/lib/holocene/keyboard-shortcut/arrow-right.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | {title} 16 | -------------------------------------------------------------------------------- /src/lib/holocene/keyboard-shortcut/arrow-up.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | {title} 15 | -------------------------------------------------------------------------------- /src/lib/holocene/labs-mode-guard.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | {#if $labsMode} 18 | 19 | {:else} 20 | 21 | {/if} 22 | -------------------------------------------------------------------------------- /src/lib/holocene/menu/index.ts: -------------------------------------------------------------------------------- 1 | import MenuButton from './menu-button.svelte'; 2 | import MenuContainer from './menu-container.svelte'; 3 | import MenuDivider from './menu-divider.svelte'; 4 | import MenuItem from './menu-item.svelte'; 5 | import Menu from './menu.svelte'; 6 | 7 | export { MenuContainer, Menu, MenuItem, MenuButton, MenuDivider }; 8 | -------------------------------------------------------------------------------- /src/lib/holocene/menu/menu-divider.svelte: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/lib/holocene/orderable-list/orderable-list.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
6 |
7 | Items 8 |
9 |
    10 | 11 | 12 | 13 |
14 |
15 | 16 | 25 | -------------------------------------------------------------------------------- /src/lib/holocene/outside-click.ts: -------------------------------------------------------------------------------- 1 | import type { Action } from 'svelte/action'; 2 | 3 | export const clickoutside: Action void> = ( 4 | node: Element, 5 | handler: (event: MouseEvent) => void, 6 | ): { destroy: () => void } => { 7 | const handleClick = (event: MouseEvent) => { 8 | if ( 9 | node && 10 | !node.contains(event.target as HTMLElement) && 11 | !event.defaultPrevented 12 | ) { 13 | handler(event); 14 | } 15 | }; 16 | 17 | document.addEventListener('click', handleClick, true); 18 | 19 | return { 20 | destroy() { 21 | document.removeEventListener('click', handleClick, true); 22 | }, 23 | }; 24 | }; 25 | -------------------------------------------------------------------------------- /src/lib/holocene/page-transition.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
9 | 10 |
11 | -------------------------------------------------------------------------------- /src/lib/holocene/pill-container/pill-container.stories.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 17 | 18 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/lib/holocene/progress-bar.stories.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/lib/holocene/radio-input/index.ts: -------------------------------------------------------------------------------- 1 | import RadioGroup, { RADIO_GROUP_CONTEXT } from './radio-group.svelte'; 2 | import RadioInput from './radio-input.svelte'; 3 | 4 | export type { RadioGroupContext, RadioInputProps } from './types'; 5 | 6 | export { RADIO_GROUP_CONTEXT, RadioGroup, RadioInput }; 7 | -------------------------------------------------------------------------------- /src/lib/holocene/radio-input/types.ts: -------------------------------------------------------------------------------- 1 | import type { HTMLAttributes, HTMLInputAttributes } from 'svelte/elements'; 2 | import type { Writable } from 'svelte/store'; 3 | 4 | export interface RadioInputProps extends HTMLInputAttributes { 5 | value: T; 6 | id: string; 7 | label: string; 8 | labelHidden?: boolean; 9 | description?: string; 10 | group?: Writable; 11 | name?: string; 12 | 'data-testid'?: string; 13 | class?: string; 14 | } 15 | 16 | export interface RadioGroupProps extends HTMLAttributes { 17 | name: string; 18 | group: Writable; 19 | description?: string; 20 | } 21 | 22 | export type RadioGroupContext = { 23 | name: string; 24 | group: Writable; 25 | }; 26 | -------------------------------------------------------------------------------- /src/lib/holocene/scroll-to-bottom.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 |
17 | 18 | 23 | -------------------------------------------------------------------------------- /src/lib/holocene/scroll-to-top.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
9 |
17 | -------------------------------------------------------------------------------- /src/lib/holocene/select/option-group.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {label} 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/lib/holocene/select/simple-option.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/lib/holocene/skeleton/index.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 |
14 | 15 | 32 | -------------------------------------------------------------------------------- /src/lib/holocene/skeleton/skeleton.stories.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/lib/holocene/tab-buttons/tab-buttons.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | -------------------------------------------------------------------------------- /src/lib/holocene/tab/tab-list.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |
22 | 23 |
24 | 25 | 30 | -------------------------------------------------------------------------------- /src/lib/holocene/tab/tab-panel.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
29 | 30 |
31 | -------------------------------------------------------------------------------- /src/lib/holocene/table/table-header-row.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/lib/holocene/table/table-row.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/lib/holocene/test-utilities.ts: -------------------------------------------------------------------------------- 1 | import type { Story } from '@storybook/addon-svelte-csf'; 2 | import { expect, within } from '@storybook/test'; 3 | 4 | type PlayFunction = Story['$$prop_def']['play']; 5 | 6 | export const shouldNotBeTransparent = ( 7 | fn: (canvas: ReturnType) => PlayFunction, 8 | ) => { 9 | return ({ canvasElement }) => { 10 | const canvas = within(canvasElement); 11 | const element = fn(canvas); 12 | expect(element).not.toHaveStyle({ backgroundColor: 'rgba(0,0,0,0)' }); 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/holocene/toggle-button/toggle-buttons.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | 8 |
9 | -------------------------------------------------------------------------------- /src/lib/holocene/user-menu-mobile.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | {#if $authUser.accessToken} 10 |
11 | 18 | 25 | {/if} 26 | -------------------------------------------------------------------------------- /src/lib/i18n/locales/en/date-picker.ts: -------------------------------------------------------------------------------- 1 | export const Namespace = 'date-picker' as const; 2 | 3 | export const Strings = { 4 | 'next-month': 'Next Month', 5 | 'previous-month': 'Previous Month', 6 | } as const; 7 | -------------------------------------------------------------------------------- /src/lib/i18n/locales/index.ts: -------------------------------------------------------------------------------- 1 | import { EN, English } from './en'; 2 | 3 | export default { 4 | [EN]: English, 5 | }; 6 | -------------------------------------------------------------------------------- /src/lib/i18n/translate.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | {#if translated !== key} 19 | {translated} 20 | {:else} 21 | 22 | {/if} 23 | -------------------------------------------------------------------------------- /src/lib/i18n/translate.ts: -------------------------------------------------------------------------------- 1 | import { t } from 'i18next'; 2 | 3 | import type { I18nKey, I18nReplace, I18nResources } from '.'; 4 | 5 | const translateGeneric = ( 6 | key: I18nKey, 7 | replace: I18nReplace = {}, 8 | ): string => { 9 | const [namespace, ...keys] = key.split('.'); 10 | 11 | if (namespace && keys.length > 0) { 12 | const k = keys.join('.'); 13 | return t(`${namespace}:${k}`, replace); 14 | } 15 | }; 16 | 17 | export const createTranslate = () => { 18 | return translateGeneric; 19 | }; 20 | 21 | export const translate = createTranslate(); 22 | -------------------------------------------------------------------------------- /src/lib/layouts/workflow-padded-layout.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | -------------------------------------------------------------------------------- /src/lib/models/core-user.ts: -------------------------------------------------------------------------------- 1 | // Actions an authenticated user can do 2 | // Continue to add to Interface as more actions are added in core 3 | export interface CoreUser { 4 | namespaceWriteDisabled: (namespace: string) => boolean; 5 | } 6 | 7 | // Set context with this key 8 | export const CoreUserKey = 'CoreUser' as const; 9 | -------------------------------------------------------------------------------- /src/lib/models/event-groups/get-group-for-event.ts: -------------------------------------------------------------------------------- 1 | import type { WorkflowEvent } from '$lib/types/events'; 2 | 3 | import type { EventGroup, EventGroups } from './event-groups'; 4 | 5 | export const getGroupForEvent = ( 6 | event: WorkflowEvent, 7 | groups: EventGroups, 8 | ): EventGroup => { 9 | const eventId = event.id; 10 | 11 | for (const group of groups) { 12 | if (eventId === group.id) return group; 13 | for (const id of group.eventIds) { 14 | if (eventId === id) { 15 | return group; 16 | } 17 | } 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/models/event-groups/get-last-event.ts: -------------------------------------------------------------------------------- 1 | import type { WorkflowEvent } from '$lib/types/events'; 2 | 3 | import type { EventGroup } from './event-groups'; 4 | 5 | export const getLastEvent = ({ events }: EventGroup): WorkflowEvent => { 6 | let latestEventKey = 0; 7 | let result: WorkflowEvent; 8 | 9 | for (const event of events.values()) { 10 | const k = Number(event.id); 11 | if (k >= latestEventKey) { 12 | latestEventKey = k; 13 | result = event; 14 | } 15 | } 16 | 17 | return result; 18 | }; 19 | -------------------------------------------------------------------------------- /src/lib/models/search-attribute-filters.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | SearchAttributes, 3 | SearchAttributeType, 4 | } from '$lib/types/workflows'; 5 | 6 | export type SearchAttributeFilter = { 7 | attribute: Extract; 8 | type: SearchAttributeType; 9 | value: string; 10 | operator: string; 11 | parenthesis: string; 12 | conditional: string; 13 | customDate?: boolean; 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/models/workflow-actions.ts: -------------------------------------------------------------------------------- 1 | export enum Action { 2 | Cancel, 3 | Reset, 4 | Terminate, 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/models/workflow-status.ts: -------------------------------------------------------------------------------- 1 | import type { WorkflowStatus } from '$lib/types/workflows'; 2 | 3 | export type WorkflowFilters = readonly (WorkflowStatus | 'All')[]; 4 | 5 | export const workflowStatuses: Readonly = [ 6 | 'Running', 7 | 'TimedOut', 8 | 'Completed', 9 | 'Failed', 10 | 'ContinuedAsNew', 11 | 'Canceled', 12 | 'Terminated', 13 | ] as const; 14 | 15 | export function isWorkflowStatusType(value: string): value is WorkflowStatus { 16 | return workflowStatuses.includes(value as WorkflowStatus); 17 | } 18 | 19 | export const workflowStatusFilters: WorkflowFilters = [ 20 | 'All', 21 | ...workflowStatuses, 22 | ] as const; 23 | -------------------------------------------------------------------------------- /src/lib/pages/workflow-history-json.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 16 | 17 | -------------------------------------------------------------------------------- /src/lib/pages/workflow-history.svelte: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /src/lib/services/settings-service.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import { isCloudMatch } from './settings-service'; 4 | 5 | describe('isCloudMatch', () => { 6 | it('should return true for tmprl.cloud', () => { 7 | expect(isCloudMatch.test('tmprl.cloud')).toBe(true); 8 | }); 9 | 10 | it('should return true for tmprl-test.cloud', () => { 11 | expect(isCloudMatch.test('tmprl.cloud')).toBe(true); 12 | }); 13 | 14 | it('should return false for non Temporal domains', () => { 15 | expect(isCloudMatch.test(undefined as unknown as string)).toBe(false); 16 | expect(isCloudMatch.test('xxx.xxx')).toBe(false); 17 | expect(isCloudMatch.test('localhost:3000')).toBe(false); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/lib/stores/auth-user.ts: -------------------------------------------------------------------------------- 1 | import { get } from 'svelte/store'; 2 | 3 | import { persistStore } from '$lib/stores/persist-store'; 4 | import type { User } from '$lib/types/global'; 5 | 6 | export const authUser = persistStore('AuthUser', {}); 7 | 8 | export const getAuthUser = (): User => get(authUser); 9 | 10 | export const setAuthUser = (user: User) => { 11 | const { accessToken, idToken, name, email, picture } = user; 12 | 13 | if (!accessToken) { 14 | throw new Error('No access token'); 15 | } 16 | 17 | authUser.set({ 18 | accessToken, 19 | idToken, 20 | name, 21 | email, 22 | picture, 23 | }); 24 | }; 25 | 26 | export const clearAuthUser = () => { 27 | authUser.set({}); 28 | }; 29 | -------------------------------------------------------------------------------- /src/lib/stores/batch-operations.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | import { pollBatchOperation } from '$lib/services/batch-service'; 4 | import { persistStore } from '$lib/stores/persist-store'; 5 | 6 | export const inProgressBatchOperation = writable<{ 7 | namespace: string; 8 | jobId: string; 9 | }>(); 10 | 11 | inProgressBatchOperation.subscribe(async (operation) => { 12 | if (operation) { 13 | await pollBatchOperation(operation).then(() => 14 | inProgressBatchOperation.set(undefined), 15 | ); 16 | } 17 | }); 18 | 19 | export const autoRefresh = persistStore( 20 | 'auto-refresh-batch-operation', 21 | false, 22 | ); 23 | -------------------------------------------------------------------------------- /src/lib/stores/bulk-actions.ts: -------------------------------------------------------------------------------- 1 | import { derived } from 'svelte/store'; 2 | 3 | import { bulkActionsEnabled } from '$lib/utilities/bulk-actions-enabled'; 4 | import { isVersionNewer } from '$lib/utilities/version-check'; 5 | 6 | import { isCloud, supportsAdvancedVisibility } from './advanced-visibility'; 7 | import { settings } from './settings'; 8 | import { temporalVersion } from './versions'; 9 | 10 | export const supportsBulkActions = derived( 11 | [temporalVersion, supportsAdvancedVisibility, settings, isCloud], 12 | ([$temporalVersion, $supportsAdvancedVisibility, $settings, $isCloud]) => 13 | ($isCloud ? true : isVersionNewer($temporalVersion, '1.18.0')) && 14 | $supportsAdvancedVisibility && 15 | bulkActionsEnabled($settings), 16 | ); 17 | -------------------------------------------------------------------------------- /src/lib/stores/capability-enablement.ts: -------------------------------------------------------------------------------- 1 | import { derived } from 'svelte/store'; 2 | 3 | import { page } from '$app/stores'; 4 | 5 | import { minimumVersionRequired } from '$lib/utilities/version-check'; 6 | 7 | import { temporalVersion } from './versions'; 8 | 9 | export const prefixSearchEnabled = derived( 10 | [page, temporalVersion], 11 | ([$page, $temporalVersion]) => { 12 | const serverVersionEnabled = minimumVersionRequired( 13 | '1.23.0', 14 | $temporalVersion, 15 | ); 16 | const capabilitiesEnabled = Boolean( 17 | $page.data?.systemInfo?.capabilities?.prefixSearch, 18 | ); 19 | return serverVersionEnabled || capabilitiesEnabled; 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /src/lib/stores/cluster.ts: -------------------------------------------------------------------------------- 1 | import { derived } from 'svelte/store'; 2 | 3 | import { page } from '$app/stores'; 4 | 5 | export const cluster = derived([page], ([$page]) => { 6 | return $page.data?.cluster; 7 | }); 8 | -------------------------------------------------------------------------------- /src/lib/stores/column-width.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const workflowEventsColumnWidth = writable(0); 4 | export const workflowEventsResponsiveColumnWidth = writable(0); 5 | -------------------------------------------------------------------------------- /src/lib/stores/core-user.ts: -------------------------------------------------------------------------------- 1 | import { readable, type Readable } from 'svelte/store'; 2 | 3 | import { getContext, hasContext } from 'svelte'; 4 | 5 | import { type CoreUser, CoreUserKey } from '$lib/models/core-user'; 6 | 7 | export const defaultCoreUserStore: Readable = readable({ 8 | namespaceWriteDisabled: () => false, 9 | }); 10 | 11 | export const coreUserStore = (): Readable => { 12 | if (!hasContext(CoreUserKey)) return defaultCoreUserStore; 13 | return getContext(CoreUserKey); 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/stores/data-converter-config.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | import type { DataEncoderStatus } from '$lib/types/global'; 4 | 5 | export const lastDataConverterStatus = 6 | writable('notRequested'); 7 | 8 | export function setLastDataConverterFailure(error?: string): void { 9 | lastDataConverterStatus.set('error'); 10 | if (error) console.error(error); 11 | } 12 | 13 | export function setLastDataConverterSuccess(): void { 14 | lastDataConverterStatus.set('success'); 15 | } 16 | 17 | export function resetLastDataConverterSuccess(): void { 18 | lastDataConverterStatus.set('notRequested'); 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/stores/error.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | import type { NetworkError } from '$lib/types/global'; 4 | 5 | export const networkError = writable(null); 6 | -------------------------------------------------------------------------------- /src/lib/stores/import-events.test.ts: -------------------------------------------------------------------------------- 1 | import { get } from 'svelte/store'; 2 | 3 | import { describe, expect, it } from 'vitest'; 4 | 5 | import { importEventGroups, importEvents } from './import-events'; 6 | 7 | describe('ImportEvents', () => { 8 | it('should get default values', () => { 9 | expect(get(importEvents)).toEqual([]); 10 | expect(get(importEventGroups)).toEqual([]); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/lib/stores/import-events.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | import type { EventGroups } from '$lib/models/event-groups/event-groups'; 4 | import type { WorkflowEvents } from '$lib/types/events'; 5 | 6 | export const importEvents = writable([]); 7 | export const importEventGroups = writable([]); 8 | -------------------------------------------------------------------------------- /src/lib/stores/labs-mode.ts: -------------------------------------------------------------------------------- 1 | import { persistStore } from './persist-store'; 2 | 3 | export const labsMode = persistStore('labsMode', false, true); 4 | -------------------------------------------------------------------------------- /src/lib/stores/namespaces.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | import type { DescribeNamespaceResponse } from '$lib/types'; 4 | 5 | import { persistStore } from './persist-store'; 6 | 7 | export const lastUsedNamespace = persistStore('lastNamespace', 'default', true); 8 | 9 | export const namespaces = writable([]); 10 | -------------------------------------------------------------------------------- /src/lib/stores/nav-open.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | import { persistStore } from './persist-store'; 4 | 5 | export const navOpen = persistStore('navOpen', false); 6 | 7 | export const namespaceSelectorOpen = writable(); 8 | -------------------------------------------------------------------------------- /src/lib/stores/new-feature-tags.ts: -------------------------------------------------------------------------------- 1 | import { get } from 'svelte/store'; 2 | 3 | import { persistStore } from './persist-store'; 4 | 5 | export const viewedFeatureTags = persistStore('viewedFeatureTags', null); 6 | 7 | export const viewFeature = (feature: string): void => { 8 | let featureTags: string[] = get(viewedFeatureTags) ?? []; 9 | if (!featureTags.includes(feature)) { 10 | featureTags = [...featureTags, feature]; 11 | viewedFeatureTags.set(featureTags); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /src/lib/stores/previous-events.ts: -------------------------------------------------------------------------------- 1 | import { writable, type Writable } from 'svelte/store'; 2 | 3 | import type { FetchEventsParameters } from '$lib/services/events-service'; 4 | 5 | const emptyPrevious: FetchEventsParameters = { 6 | namespace: null, 7 | workflowId: null, 8 | runId: null, 9 | rawPayloads: null, 10 | sort: null, 11 | }; 12 | 13 | export const previous: Writable = 14 | writable(emptyPrevious); 15 | 16 | export const clearPreviousEventParameters = (): void => { 17 | previous.set(emptyPrevious); 18 | }; 19 | -------------------------------------------------------------------------------- /src/lib/stores/reset-workflows.ts: -------------------------------------------------------------------------------- 1 | import { persistStore } from './persist-store'; 2 | 3 | export const resetWorkflows = persistStore>( 4 | 'resetWorkflows', 5 | {}, 6 | ); 7 | -------------------------------------------------------------------------------- /src/lib/stores/settings.ts: -------------------------------------------------------------------------------- 1 | import { derived } from 'svelte/store'; 2 | 3 | import { page } from '$app/stores'; 4 | 5 | export const settings = derived([page], ([$page]) => $page.data.settings); 6 | -------------------------------------------------------------------------------- /src/lib/stores/task-queue-view.ts: -------------------------------------------------------------------------------- 1 | import { persistStore } from '$lib/stores/persist-store'; 2 | import type { TaskQueueView } from '$lib/types/events'; 3 | 4 | export const taskQueueView = persistStore( 5 | 'taskQueueView', 6 | 'workers', 7 | true, 8 | ); 9 | -------------------------------------------------------------------------------- /src/lib/stores/versions.ts: -------------------------------------------------------------------------------- 1 | import { derived } from 'svelte/store'; 2 | 3 | import { cluster } from './cluster'; 4 | import { settings } from './settings'; 5 | 6 | export const temporalVersion = derived([cluster], ([$cluster]) => { 7 | return $cluster?.serverVersion; 8 | }); 9 | 10 | export const uiVersion = derived([settings], ([$settings]) => { 11 | return $settings?.version; 12 | }); 13 | -------------------------------------------------------------------------------- /src/lib/svelte-mocks/app/environment.ts: -------------------------------------------------------------------------------- 1 | export function browser(): boolean { 2 | return true; 3 | } 4 | -------------------------------------------------------------------------------- /src/lib/svelte-mocks/app/paths.ts: -------------------------------------------------------------------------------- 1 | export const base = ''; 2 | -------------------------------------------------------------------------------- /src/lib/theme/preset.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | 3 | import temporal, { textStyles } from './plugin'; 4 | 5 | const config = { 6 | content: ['./src/**/*.{html,js,svelte,ts}'], 7 | plugins: [temporal, textStyles], 8 | } satisfies Config; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /src/lib/theme/types.d.ts: -------------------------------------------------------------------------------- 1 | type PaletteColor = import('./colors').PaletteColor; 2 | type Palette = import('./colors').Palette; 3 | 4 | type RGB = `${number} ${number} ${number}`; 5 | type HexColor = `#${string}`; 6 | 7 | type CSSVariable = `--${string}`; 8 | type ColorVariables = Readonly< 9 | Record 10 | >; 11 | 12 | type Shade = 13 | | 50 14 | | 100 15 | | 200 16 | | 300 17 | | 400 18 | | 500 19 | | 600 20 | | 700 21 | | 800 22 | | 900 23 | | 950 24 | | 'DEFAULT'; 25 | 26 | type Shades = Record; 27 | 28 | type Color = [PaletteColor, Shade | undefined] | HexColor; 29 | 30 | type ColorName = 31 | | Exclude 32 | | `${PaletteColor}.${Shade}`; 33 | -------------------------------------------------------------------------------- /src/lib/types/holocene.ts: -------------------------------------------------------------------------------- 1 | export type DataAttributes = { 2 | // [index: `data-${string}`]: any; 3 | 'data-testid'?: string; 4 | }; 5 | 6 | export type ToastVariant = 'success' | 'error' | 'info' | 'warning' | 'primary'; 7 | 8 | export type ToastPosition = 9 | | 'top-left' 10 | | 'top-center' 11 | | 'top-right' 12 | | 'bottom-left' 13 | | 'bottom-center' 14 | | 'bottom-right'; 15 | 16 | export interface Toast { 17 | message: string; 18 | variant?: ToastVariant; 19 | id?: string; 20 | duration?: number; 21 | link?: string; 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/types/nexus.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CallbackInfo, 3 | Endpoint, 4 | EndpointSpec, 5 | Callback as ICallback, 6 | } from '$lib/types'; 7 | 8 | import type { EventLink } from './events'; 9 | 10 | export interface NexusEndpointSpec extends EndpointSpec { 11 | descriptionString?: string; 12 | allowedCallerNamespaces?: string[]; 13 | } 14 | export interface NexusEndpoint extends Endpoint { 15 | asyncOperationId?: string; 16 | state?: string; 17 | spec?: NexusEndpointSpec; 18 | } 19 | 20 | export interface Callback extends CallbackInfo { 21 | blockedReason?: string; 22 | callback?: CallbackWithLinks; 23 | } 24 | interface CallbackWithLinks extends ICallback { 25 | links?: EventLink[]; 26 | } 27 | -------------------------------------------------------------------------------- /src/lib/utilities/advanced-visibility-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { ClusterInformation } from '$lib/types/global'; 2 | 3 | import { isVersionNewer } from './version-check'; 4 | 5 | export const advancedVisibilityEnabled = ( 6 | cluster: ClusterInformation, 7 | version: string, 8 | ) => { 9 | return ( 10 | cluster?.visibilityStore?.includes('elasticsearch') || 11 | isVersionNewer(version, '1.19') 12 | ); 13 | }; 14 | 15 | export const advancedVisibilityEnabledWithOrderBy = ( 16 | cluster: ClusterInformation, 17 | ) => { 18 | return cluster?.visibilityStore?.includes('elasticsearch'); 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/utilities/btoa.ts: -------------------------------------------------------------------------------- 1 | import { BROWSER } from 'esm-env'; 2 | 3 | export const base64EncodeUnicode = (str: string) => { 4 | return window.btoa( 5 | encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, p1) => 6 | String.fromCharCode(parseInt(p1, 16)), 7 | ), 8 | ); 9 | }; 10 | 11 | export const btoa = (str: string, isBrowser = BROWSER): string => { 12 | if (!isBrowser) return str; 13 | return base64EncodeUnicode(str); 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/utilities/bulk-actions-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { Settings } from '$lib/types/global'; 2 | 3 | const ALLOWED_BULK_ACTIONS: (keyof Pick< 4 | Settings, 5 | | 'workflowSignalDisabled' 6 | | 'workflowCancelDisabled' 7 | | 'workflowResetDisabled' 8 | | 'workflowTerminateDisabled' 9 | >)[] = ['workflowCancelDisabled', 'workflowTerminateDisabled']; 10 | 11 | export const bulkActionsEnabled = (settings: Settings) => { 12 | if (settings.disableWriteActions) return false; 13 | if (settings.batchActionsDisabled) return false; 14 | 15 | return ALLOWED_BULK_ACTIONS.some((action) => !settings[action]); 16 | }; 17 | -------------------------------------------------------------------------------- /src/lib/utilities/cancel-in-progress.ts: -------------------------------------------------------------------------------- 1 | import type { WorkflowEvents } from '$lib/types/events'; 2 | import type { WorkflowStatus } from '$lib/types/workflows'; 3 | 4 | export const isCancelInProgress = ( 5 | status: WorkflowStatus, 6 | eventHistory: WorkflowEvents, 7 | ) => { 8 | const isRunning = status === 'Running'; 9 | const workflowCancelRequested = eventHistory?.some( 10 | (event) => event?.eventType === 'WorkflowExecutionCancelRequested', 11 | ); 12 | return isRunning && workflowCancelRequested; 13 | }; 14 | -------------------------------------------------------------------------------- /src/lib/utilities/copy-to-clipboard.ts: -------------------------------------------------------------------------------- 1 | import { type Writable, writable } from 'svelte/store'; 2 | 3 | type CopiedToClipboardReturnValue = { 4 | copy: (event: Event, content: string) => Promise; 5 | copied: Writable; 6 | }; 7 | 8 | export const copyToClipboard = ( 9 | timeout = 2000, 10 | ): CopiedToClipboardReturnValue => { 11 | const copied = writable(false); 12 | 13 | const copy = async (event: Event, content: string) => { 14 | event.preventDefault(); 15 | event.stopPropagation(); 16 | try { 17 | await navigator.clipboard.writeText(content); 18 | copied.set(true); 19 | setTimeout(() => { 20 | copied.set(false); 21 | }, timeout); 22 | } catch (error) { 23 | console.error(error); 24 | } 25 | }; 26 | 27 | return { copy, copied }; 28 | }; 29 | -------------------------------------------------------------------------------- /src/lib/utilities/dark-mode/dark-mode.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/lib/utilities/dark-mode/index.ts: -------------------------------------------------------------------------------- 1 | import DarkMode from './dark-mode.svelte'; 2 | 3 | export default DarkMode; 4 | export { 5 | useDarkMode, 6 | useDarkModePreference, 7 | getNextDarkModePreference, 8 | } from './dark-mode'; 9 | 10 | export type { DarkModePreference } from './dark-mode'; 11 | -------------------------------------------------------------------------------- /src/lib/utilities/encode-uri.ts: -------------------------------------------------------------------------------- 1 | export function encodeURIForSvelte(uri: string): string { 2 | if (uri) return encodeURIComponent(uri); 3 | return uri; 4 | } 5 | 6 | export function decodeURIForSvelte(uri: string): string { 7 | if (uri) return decodeURIComponent(uri); 8 | return uri; 9 | } 10 | -------------------------------------------------------------------------------- /src/lib/utilities/event-formatting.ts: -------------------------------------------------------------------------------- 1 | import type { EventSortOrder } from '$lib/stores/event-view'; 2 | import type { BooleanString } from '$lib/types/global'; 3 | 4 | type DateFilterOptions = { 5 | compact: boolean; 6 | sortOrder: EventSortOrder; 7 | showElapsed: BooleanString; 8 | }; 9 | 10 | export const getDateFilterValue = ({ 11 | compact, 12 | sortOrder, 13 | showElapsed, 14 | }: DateFilterOptions) => { 15 | const isDefaultSortOrder = compact || sortOrder === 'descending'; 16 | const isNotElapsedTime = showElapsed === 'false'; 17 | const allDefaults = isDefaultSortOrder && isNotElapsedTime; 18 | 19 | if (allDefaults) { 20 | return undefined; 21 | } 22 | 23 | return `${sortOrder}:${showElapsed}`; 24 | }; 25 | -------------------------------------------------------------------------------- /src/lib/utilities/export-workflows.ts: -------------------------------------------------------------------------------- 1 | import type { WorkflowExecution } from '@temporalio/common'; 2 | 3 | import { stringifyWithBigInt } from './parse-with-big-int'; 4 | 5 | export const exportWorkflows = ( 6 | workflows: WorkflowExecution[], 7 | page: number, 8 | ) => { 9 | const content = stringifyWithBigInt({ workflows }, null, 2); 10 | const fileName = `workflows-${workflows.length}-${page}-${Date.now()}.json`; 11 | download(content, fileName, 'text/plain'); 12 | 13 | function download(content: string, fileName: string, contentType: string) { 14 | const a = document.createElement('a'); 15 | const file = new Blob([content], { type: contentType }); 16 | a.href = URL.createObjectURL(file); 17 | a.download = fileName; 18 | a.click(); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /src/lib/utilities/format-bytes.ts: -------------------------------------------------------------------------------- 1 | export const formatBytes = (bytes: number, decimals = 2) => { 2 | if (!+bytes) return '0 Bytes'; 3 | 4 | const k = 1024; 5 | const dm = decimals < 0 ? 0 : decimals; 6 | const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 7 | 8 | const i = Math.floor(Math.log(bytes) / Math.log(k)); 9 | 10 | return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/utilities/get-api-origin.ts: -------------------------------------------------------------------------------- 1 | import { BROWSER } from 'esm-env'; 2 | 3 | export function getApiOrigin(isBrowser = BROWSER): string | null { 4 | const endpoint = import.meta.env.VITE_API; 5 | const isRelative = !endpoint.startsWith('http'); 6 | 7 | let origin = ''; 8 | 9 | if (isRelative) { 10 | origin = isBrowser ? window.location.origin + endpoint : ''; 11 | } else { 12 | origin = endpoint; 13 | } 14 | 15 | if (origin.endsWith('/')) origin = origin.slice(0, -1); 16 | 17 | return origin; 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/utilities/get-clusters.ts: -------------------------------------------------------------------------------- 1 | import type { DescribeNamespaceResponse } from '$lib/types'; 2 | 3 | export const getClusters = (namespace: DescribeNamespaceResponse): string => { 4 | const clusters = namespace?.replicationConfig?.clusters; 5 | const activeCluster = namespace?.replicationConfig?.activeClusterName; 6 | if (clusters?.length) { 7 | return clusters 8 | .map(({ clusterName }) => { 9 | if (clusterName === activeCluster) { 10 | return `${clusterName} (active)`; 11 | } 12 | return clusterName; 13 | }) 14 | .join(', '); 15 | } 16 | return 'Unknown'; 17 | }; 18 | -------------------------------------------------------------------------------- /src/lib/utilities/get-context.ts: -------------------------------------------------------------------------------- 1 | import { getContext } from 'svelte'; 2 | 3 | export function getAppContext(key: 'group'): boolean; 4 | export function getAppContext(key: string): unknown { 5 | return getContext(key); 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/utilities/get-environment.ts: -------------------------------------------------------------------------------- 1 | export function getEnvironment(): string | null { 2 | return import.meta.env.VITE_TEMPORAL_UI_BUILD_TARGET ?? null; 3 | } 4 | -------------------------------------------------------------------------------- /src/lib/utilities/get-float-style.ts: -------------------------------------------------------------------------------- 1 | export const getFloatStyle = ({ 2 | width, 3 | height, 4 | screenWidth, 5 | breakpoint = 1279, // 'xl' max-width breakpoint in tailwindcss 6 | }: { 7 | width?: number; 8 | height?: number; 9 | screenWidth: number; 10 | breakpoint?: number; 11 | }): string => { 12 | return width && height && screenWidth > breakpoint 13 | ? `position: absolute; right: ${width + 20}px; top: -${height}px` 14 | : ''; 15 | }; 16 | -------------------------------------------------------------------------------- /src/lib/utilities/get-group-status-and-count.ts: -------------------------------------------------------------------------------- 1 | import { workflowStatuses } from '$lib/models/workflow-status'; 2 | import type { WorkflowStatus } from '$lib/types/workflows'; 3 | 4 | import { decodePayload } from './decode-payload'; 5 | 6 | export const getStatusAndCountOfGroup = (groups = []) => { 7 | return groups 8 | .map((group) => { 9 | const status = decodePayload( 10 | group?.groupValues[0], 11 | ) as unknown as WorkflowStatus; 12 | const count = parseInt(group.count); 13 | return { 14 | status, 15 | count, 16 | }; 17 | }) 18 | .sort((a, b) => { 19 | return ( 20 | workflowStatuses.indexOf(a.status) - workflowStatuses.indexOf(b.status) 21 | ); 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /src/lib/utilities/get-namespace.ts: -------------------------------------------------------------------------------- 1 | import type { DescribeNamespaceResponse } from '$lib/types'; 2 | 3 | type GetNamespaceParameters = { 4 | namespace?: string; 5 | defaultNamespace: string; 6 | namespaces: DescribeNamespaceResponse[]; 7 | }; 8 | 9 | export const getNamespace = ({ 10 | namespace, 11 | defaultNamespace, 12 | namespaces, 13 | }: GetNamespaceParameters): string | undefined => { 14 | if (!namespace) return defaultNamespace; 15 | if (!namespaces?.length) return defaultNamespace; 16 | 17 | if (namespaces.find((ns) => ns?.namespaceInfo?.name === namespace)) { 18 | return namespace; 19 | } 20 | 21 | return undefined; 22 | }; 23 | -------------------------------------------------------------------------------- /src/lib/utilities/get-query-types-from-error.ts: -------------------------------------------------------------------------------- 1 | export const getQueryTypesFromError = (message: string): { name: string }[] => { 2 | const indexOfOpeningBracket = message.indexOf('['); 3 | const indexOfClosingBracket = message.indexOf(']'); 4 | 5 | return message 6 | .slice(indexOfOpeningBracket + 1, indexOfClosingBracket) 7 | .split(' ') 8 | .filter((query: string) => query !== '__stack_trace') 9 | .map((query: string) => { 10 | if (query.endsWith(',')) { 11 | return { name: query.slice(0, query.length - 1) }; 12 | } else { 13 | return { name: query }; 14 | } 15 | }); 16 | }; 17 | -------------------------------------------------------------------------------- /src/lib/utilities/get-truncated-word.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import { getTruncatedWord } from './get-truncated-word'; 4 | 5 | describe('getTruncatedWord', () => { 6 | it('should return same word if the width is wider than word', () => { 7 | expect(getTruncatedWord('Running', 100)).toBe('Running'); 8 | }); 9 | it('should return truncated word if the width is shorter than word', () => { 10 | expect(getTruncatedWord('Running', 50)).toBe('Ru...'); 11 | }); 12 | it('should return "..." if the width is zero', () => { 13 | expect(getTruncatedWord('Running', 0)).toBe('...'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/lib/utilities/get-truncated-word.ts: -------------------------------------------------------------------------------- 1 | export const getTruncatedWord = (word: string, width: number): string => { 2 | if (word.length * 8.15 > width) { 3 | const truncLength = Math.floor(width / 8.15) - 4; 4 | if (truncLength > 0) { 5 | const trunc = word.slice(0, truncLength); 6 | return `${trunc}...`; 7 | } else { 8 | return '...'; 9 | } 10 | } 11 | return word; 12 | }; 13 | -------------------------------------------------------------------------------- /src/lib/utilities/get-workflow-status-filter-code.ts: -------------------------------------------------------------------------------- 1 | import type { WorkflowStatus } from '$lib/types/workflows'; 2 | 3 | type ExecutionStatusCodes = '1' | '2' | '3' | '4' | '5' | '6' | '7'; 4 | 5 | export const getStatusFilterCode = ( 6 | status: WorkflowStatus, 7 | ): ExecutionStatusCodes => { 8 | if (status === 'Running') return '1'; 9 | if (status === 'Completed') return '2'; 10 | if (status === 'Failed') return '3'; 11 | if (status === 'Canceled') return '4'; 12 | if (status === 'Terminated') return '5'; 13 | if (status === 'ContinuedAsNew') return '6'; 14 | if (status === 'TimedOut') return '7'; 15 | }; 16 | -------------------------------------------------------------------------------- /src/lib/utilities/has.ts: -------------------------------------------------------------------------------- 1 | import { isObject } from './is'; 2 | 3 | export const has = , V = unknown>( 4 | target: unknown, 5 | ...properties: K 6 | ): target is Record => { 7 | if (!hasAnyProperties(target)) return false; 8 | for (const property of properties) { 9 | if (!Object.prototype.hasOwnProperty.call(target, property)) return false; 10 | } 11 | return true; 12 | }; 13 | 14 | export const hasAnyProperties = ( 15 | obj: unknown, 16 | ): obj is ReturnType => { 17 | if (!isObject(obj)) return false; 18 | return !!Object.getOwnPropertyNames(obj).length; 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/utilities/is-authorized.ts: -------------------------------------------------------------------------------- 1 | import type { Settings, User } from '$lib/types/global'; 2 | 3 | export const isAuthorized = (settings: Settings, user: User): boolean => { 4 | return !settings.auth.enabled || Boolean(user?.accessToken); 5 | }; 6 | -------------------------------------------------------------------------------- /src/lib/utilities/is-email.ts: -------------------------------------------------------------------------------- 1 | // http://emailregex.com/ 2 | export const isEmail = (value: string): boolean => { 3 | return /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( 4 | value, 5 | ); 6 | }; 7 | -------------------------------------------------------------------------------- /src/lib/utilities/is-function.ts: -------------------------------------------------------------------------------- 1 | export const isFunction = (fn: unknown): fn is T => { 2 | return typeof fn === 'function'; 3 | }; 4 | -------------------------------------------------------------------------------- /src/lib/utilities/is-http.ts: -------------------------------------------------------------------------------- 1 | export const validateHttps = (endpoint: string): boolean => { 2 | return endpoint?.substring(0, 8) === 'https://'; 3 | }; 4 | 5 | export const validateHttp = (endpoint: string): boolean => { 6 | return endpoint?.substring(0, 7) === 'http://'; 7 | }; 8 | 9 | export const validateHttpOrHttps = (endpoint: string): boolean => { 10 | return validateHttp(endpoint) || validateHttps(endpoint); 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/utilities/is-network-error.ts: -------------------------------------------------------------------------------- 1 | import type { NetworkError } from '$lib/types/global'; 2 | 3 | import { has } from './has'; 4 | 5 | export function isNetworkError( 6 | error: unknown | NetworkError, 7 | ): error is NetworkError { 8 | if (!error) return false; 9 | return has(error, 'statusCode', 'statusText', 'response'); 10 | } 11 | -------------------------------------------------------------------------------- /src/lib/utilities/is-pending-activity.ts: -------------------------------------------------------------------------------- 1 | import type { PendingActivity, PendingNexusOperation } from '$lib/types/events'; 2 | 3 | import { has } from './has'; 4 | 5 | export const isPendingActivity = (event: unknown): event is PendingActivity => { 6 | if (event === null) return false; 7 | if (typeof event !== 'object') return false; 8 | if (Array.isArray(event)) return false; 9 | if (has(event, 'activityType')) return true; 10 | return false; 11 | }; 12 | 13 | export const isPendingNexusOperation = ( 14 | event: unknown, 15 | ): event is PendingNexusOperation => { 16 | if (event === null) return false; 17 | if (typeof event !== 'object') return false; 18 | if (Array.isArray(event)) return false; 19 | if (has(event, 'operation') && has(event, 'endpoint')) return true; 20 | return false; 21 | }; 22 | -------------------------------------------------------------------------------- /src/lib/utilities/merge.ts: -------------------------------------------------------------------------------- 1 | type KeyValue = { [key: string]: string | number | boolean }; 2 | 3 | export const merge = ( 4 | first: T = {} as T, 5 | second: T = {} as T, 6 | ): T => { 7 | const result = { ...first }; 8 | 9 | for (const key of Object.keys(second)) { 10 | const value = result[key]; 11 | 12 | if (Array.isArray(value)) { 13 | result[key] = result[key].concat(second[key]); 14 | } else if (typeof value === 'object' && !Array.isArray(value)) { 15 | result[key] = merge(result[key], second[key]); 16 | } else { 17 | result[key] = second[key]; 18 | } 19 | } 20 | 21 | return result; 22 | }; 23 | -------------------------------------------------------------------------------- /src/lib/utilities/namespace-url-pattern.ts: -------------------------------------------------------------------------------- 1 | import UrlPattern from 'url-pattern'; 2 | 3 | // The default for this lib doesn't include .-_ which are all valid characters for a cloud namespace 4 | const urlPatternOpts = { segmentValueCharset: '.a-zA-Z0-9_-' }; 5 | 6 | export const namespaceUrlPattern = new UrlPattern( 7 | '/namespaces/:namespace/*', 8 | urlPatternOpts, 9 | ); 10 | 11 | export const workflowRoutePattern = new UrlPattern( 12 | '/namespaces/:namespace/workflows*', 13 | urlPatternOpts, 14 | ); 15 | -------------------------------------------------------------------------------- /src/lib/utilities/nexus-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { Capabilities } from '$lib/types'; 2 | 3 | export const nexusEnabled = (capabilities: Capabilities): boolean => { 4 | return capabilities?.nexus; 5 | }; 6 | -------------------------------------------------------------------------------- /src/lib/utilities/omit.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import { omit } from './omit'; 4 | 5 | describe('omit', () => { 6 | it('should remove a key from an object', () => { 7 | const obj = { a: 1, b: 2 }; 8 | const result = omit(obj, 'b'); 9 | 10 | expect(result).toEqual({ a: 1 }); 11 | }); 12 | 13 | it('should not modify an object if given an invalid key', () => { 14 | const obj = { a: 1, b: 2 }; 15 | const result = omit(obj, 'c'); 16 | 17 | expect(result).toEqual({ a: 1, b: 2 }); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/lib/utilities/omit.ts: -------------------------------------------------------------------------------- 1 | type Omit = { 2 | // eslint-disable-next-line @typescript-eslint/ban-types 3 | ( 4 | obj: Source, 5 | ...keys: KeysToOmit 6 | ): { 7 | [RemainingKey in Exclude< 8 | keyof Source, 9 | KeysToOmit[number] 10 | >]: Source[RemainingKey]; 11 | }; 12 | }; 13 | 14 | export const omit: Omit = (object, ...keys) => { 15 | const result = {} as { 16 | [K in keyof typeof object]: (typeof object)[K]; 17 | }; 18 | 19 | for (const key of Object.keys(object)) { 20 | if (!keys.includes(key)) { 21 | result[key] = object[key]; 22 | } 23 | } 24 | 25 | return result; 26 | }; 27 | -------------------------------------------------------------------------------- /src/lib/utilities/parse-with-big-int.ts: -------------------------------------------------------------------------------- 1 | import JSONbig from 'json-bigint'; 2 | 3 | const JSONBigNative = JSONbig({ 4 | useNativeBigInt: true, 5 | constructorAction: 'preserve', 6 | }); 7 | 8 | export const parseWithBigInt = (content: string) => { 9 | try { 10 | return JSONBigNative.parse(content); 11 | } catch (e) { 12 | return JSON.parse(content); 13 | } 14 | }; 15 | 16 | export const stringifyWithBigInt = ( 17 | value: T, 18 | replacer?: (key: string, value: T) => T, 19 | space?: string | number, 20 | ) => JSONBigNative.stringify(value, replacer, space); 21 | -------------------------------------------------------------------------------- /src/lib/utilities/payload-to-string.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import { payloadToString } from './payload-to-string'; 4 | 5 | describe('payloadToString', () => { 6 | it('should combine payloads into a string if it is an array of payloads', () => { 7 | expect(payloadToString(['a'])).toBe('a'); 8 | expect(payloadToString(['a', 'b', 'c'])).toBe('a, b, c'); 9 | }); 10 | 11 | it('should return the payload if the payload is not an array of payloads', () => { 12 | expect(payloadToString('a')).toBe('a'); 13 | expect(payloadToString(['a, b, c'])).toBe('a, b, c'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/lib/utilities/payload-to-string.ts: -------------------------------------------------------------------------------- 1 | import type { Payload } from '$lib/types'; 2 | 3 | export const payloadToString = (value: Payload) => { 4 | if (Array.isArray(value)) return value.join(', '); 5 | 6 | return value; 7 | }; 8 | -------------------------------------------------------------------------------- /src/lib/utilities/pick.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import { pick } from './pick'; 4 | 5 | describe('pick', () => { 6 | it('should only keep the keys specified', () => { 7 | const source = { a: 1, b: 2, c: 3 }; 8 | expect(pick(source, 'a', 'c')).toEqual({ a: 1, c: 3 }); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /src/lib/utilities/pick.ts: -------------------------------------------------------------------------------- 1 | export const pick = ( 2 | source: T, 3 | ...keys: K[] 4 | ): Pick => { 5 | const result: Partial> = {}; 6 | for (const key of keys) { 7 | result[key] = source[key]; 8 | } 9 | return result as Pick; 10 | }; 11 | -------------------------------------------------------------------------------- /src/lib/utilities/pluralize.ts: -------------------------------------------------------------------------------- 1 | export const pluralize = (word: string, count: number): string => { 2 | if (count === 1) { 3 | return word; 4 | } 5 | return `${word}s`; 6 | }; 7 | -------------------------------------------------------------------------------- /src/lib/utilities/query/is-search-attribute.ts: -------------------------------------------------------------------------------- 1 | import { get } from 'svelte/store'; 2 | 3 | import { searchAttributes } from '$lib/stores/search-attributes'; 4 | 5 | import { isString } from '../is'; 6 | 7 | export const isSearchAttribute = ( 8 | attribute: string, 9 | attributes = searchAttributes, 10 | ): attribute is string => { 11 | if (!isString(attribute)) return false; 12 | return !!get(attributes)[attribute]; 13 | }; 14 | -------------------------------------------------------------------------------- /src/lib/utilities/refresh-rate.ts: -------------------------------------------------------------------------------- 1 | export const getExponentialBackoffSeconds = ( 2 | initialIntervalSeconds: number, 3 | attempt: number, 4 | maxAttempts: number, 5 | ): number => { 6 | const maxIntervalSeconds = 3600; 7 | const growthFactor = Math.pow( 8 | maxIntervalSeconds / initialIntervalSeconds, 9 | 1 / maxAttempts, 10 | ); 11 | const exponentialBackoff = 12 | initialIntervalSeconds * Math.pow(growthFactor, attempt); 13 | const intervalSeconds = Math.min( 14 | maxIntervalSeconds, 15 | Math.round(exponentialBackoff), 16 | ); 17 | return intervalSeconds; 18 | }; 19 | -------------------------------------------------------------------------------- /src/lib/utilities/search-type-parameter.ts: -------------------------------------------------------------------------------- 1 | import { isString } from './is'; 2 | 3 | type SearchType = 'basic' | 'advanced'; 4 | 5 | export const isValidSearchType = ( 6 | parameter: unknown, 7 | ): parameter is SearchType => { 8 | if (!isString(parameter)) return false; 9 | if (parameter === 'basic') return true; 10 | if (parameter === 'advanced') return true; 11 | return false; 12 | }; 13 | 14 | export const getSearchType = (url: URL): SearchType => { 15 | const searchType = url.searchParams.get('search'); 16 | 17 | if (isValidSearchType(searchType)) return searchType; 18 | 19 | url.searchParams.set('search', 'basic'); 20 | 21 | return 'basic'; 22 | }; 23 | -------------------------------------------------------------------------------- /src/lib/utilities/stack-trace/get-file-paths-from-stack-trace.ts: -------------------------------------------------------------------------------- 1 | import { getFilePathsFromGoStackTrace } from './get-file-paths-from-go-stack-trace'; 2 | import { getFilePathsFromTypeScriptStackTrace } from './get-file-paths-from-typescript-stack-trace'; 3 | import { isFromGoSDK } from './is-from-go-sdk'; 4 | import { isFromTypeScriptSDK } from './is-from-typescript-sdk'; 5 | 6 | export const getFilePathsFromStackTrace = ( 7 | stackTraceText: string, 8 | ): { filePath: string; codeLine: number; character: number }[] => { 9 | if (isFromTypeScriptSDK(stackTraceText)) { 10 | return getFilePathsFromTypeScriptStackTrace(stackTraceText); 11 | } else if (isFromGoSDK(stackTraceText)) { 12 | return getFilePathsFromGoStackTrace(stackTraceText); 13 | } else return undefined; 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/utilities/stack-trace/get-sdk-origin.ts: -------------------------------------------------------------------------------- 1 | // The function takes a decoded StackTraceQuery response as a string 2 | // and returns the name of the SDK the response came from 3 | 4 | import { isFromGoSDK } from './is-from-go-sdk'; 5 | import { isFromJavaSDK } from './is-from-java-sdk'; 6 | import { isFromTypeScriptSDK } from './is-from-typescript-sdk'; 7 | 8 | export const getSDKOrigin = (stackTraceText: string): string => { 9 | if (isFromTypeScriptSDK(stackTraceText)) return 'typescript'; 10 | else if (isFromGoSDK(stackTraceText)) return 'go'; 11 | else if (isFromJavaSDK(stackTraceText)) return 'java'; 12 | else return null; 13 | }; 14 | -------------------------------------------------------------------------------- /src/lib/utilities/stack-trace/is-from-go-sdk.ts: -------------------------------------------------------------------------------- 1 | // The function takes a decoded StackTraceQuery response as a string 2 | // It returns true if the response came from the GoSDK and false otherwise 3 | 4 | export const isFromGoSDK = (stackTraceText: string): boolean => { 5 | return /\w.go:\d/.test(stackTraceText); //contains '.go:' followed by a digit 6 | }; 7 | -------------------------------------------------------------------------------- /src/lib/utilities/stack-trace/is-from-java-sdk.ts: -------------------------------------------------------------------------------- 1 | // The function takes a decoded StackTraceQuery response as a string 2 | // It returns true if the response came from the JavaSDK and false otherwise 3 | 4 | export const isFromJavaSDK = (stackTraceText: string): boolean => { 5 | return /\w.java:\d/.test(stackTraceText); //contains '.java:' followed by a digit 6 | }; 7 | -------------------------------------------------------------------------------- /src/lib/utilities/stack-trace/is-from-typescript-sdk.ts: -------------------------------------------------------------------------------- 1 | // The function takes a decoded StackTraceQuery response as a string 2 | // It returns true if the response came from the TypeScriptSDK and false otherwise 3 | 4 | export const isFromTypeScriptSDK = (stackTraceText: string): boolean => { 5 | return /\w.ts:\d/.test(stackTraceText); //contains '.ts:' followed by a digit 6 | }; 7 | -------------------------------------------------------------------------------- /src/lib/utilities/to-time-difference.ts: -------------------------------------------------------------------------------- 1 | import type { Timestamp } from '$lib/types'; 2 | 3 | export const toTimeDifference = ({ 4 | date, 5 | now = Date.now(), 6 | negativeDefault, 7 | }: { 8 | date: Timestamp; 9 | now?: number; 10 | negativeDefault?: string; 11 | }): string => { 12 | if (!date) return ''; 13 | const start = String(date); 14 | 15 | try { 16 | const scheduled = Number(new Date(start)); 17 | const timeFromNow = (scheduled - now) / 1000; 18 | 19 | if (negativeDefault && timeFromNow < 0) { 20 | return negativeDefault; 21 | } 22 | 23 | return !isNaN(timeFromNow) ? `${timeFromNow.toFixed(0)}s` : ''; 24 | } catch (error) { 25 | return ''; 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /src/lib/utilities/to-url.ts: -------------------------------------------------------------------------------- 1 | export const toURL = ( 2 | url: string, 3 | params?: URLSearchParams | Record, 4 | hash?: string, 5 | ): string => { 6 | const isURLSearchParams = params instanceof URLSearchParams; 7 | if (params && !isURLSearchParams) params = new URLSearchParams(params); 8 | if (params) url = `${url}?${params}`; 9 | if (hash) url = `${url}#${hash}`; 10 | return url; 11 | }; 12 | -------------------------------------------------------------------------------- /src/lib/utilities/trim-trailing-slash.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import { trimTrailingSlash } from './trim-trailing-slash'; 4 | 5 | describe('trimTrailingSlash', () => { 6 | it('should remove trailing slash from a string', () => { 7 | expect(trimTrailingSlash('http://cats.meow/')).toEqual('http://cats.meow'); 8 | }); 9 | it('should return original string if no trailing slash', () => { 10 | expect(trimTrailingSlash('http://cats.meow')).toEqual('http://cats.meow'); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /src/lib/utilities/trim-trailing-slash.ts: -------------------------------------------------------------------------------- 1 | export const trimTrailingSlash = (x: string): string => { 2 | return x.replace(/\/+$/, ''); 3 | }; 4 | -------------------------------------------------------------------------------- /src/lib/utilities/unique.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest'; 2 | 3 | import { unique } from './unique'; 4 | 5 | describe('unique', () => { 6 | it('should remove duplicate values from an array', () => { 7 | expect([1, 2, 2, 3].filter(unique)).toEqual([1, 2, 3]); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/lib/utilities/unique.ts: -------------------------------------------------------------------------------- 1 | export const unique = (value: T, index: number, self: T[]): boolean => 2 | self.indexOf(value) === index; 3 | -------------------------------------------------------------------------------- /src/lib/utilities/workflow-cancel-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { CoreUser } from '$lib/models/core-user'; 2 | import type { Settings } from '$lib/types/global'; 3 | 4 | export const workflowCancelEnabled = ( 5 | settings: Settings, 6 | coreUser: CoreUser, 7 | namespace: string, 8 | ): boolean => { 9 | return ( 10 | !settings.disableWriteActions && 11 | !settings.workflowCancelDisabled && 12 | !coreUser.namespaceWriteDisabled(namespace) 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/utilities/workflow-create-disabled.ts: -------------------------------------------------------------------------------- 1 | import { get } from 'svelte/store'; 2 | 3 | import type { Page } from '@sveltejs/kit'; 4 | 5 | import { coreUserStore } from '$lib/stores/core-user'; 6 | 7 | export const workflowCreateDisabled = ( 8 | page: Page, 9 | namespace?: string, 10 | ): boolean => { 11 | const coreUser = coreUserStore(); 12 | const namespaceWriteDisabled = get(coreUser).namespaceWriteDisabled( 13 | namespace ?? page.params.namespace, 14 | ); 15 | if (page?.data?.settings?.disableWriteActions) return true; 16 | if (page?.data?.settings?.startWorkflowDisabled) return true; 17 | 18 | return namespaceWriteDisabled; 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/utilities/workflow-reset-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { CoreUser } from '$lib/models/core-user'; 2 | import type { Settings } from '$lib/types/global'; 3 | 4 | export const workflowResetEnabled = ( 5 | settings: Settings, 6 | coreUser: CoreUser, 7 | namespace: string, 8 | ): boolean => { 9 | return ( 10 | !settings.disableWriteActions && 11 | !settings.workflowResetDisabled && 12 | !coreUser.namespaceWriteDisabled(namespace) 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/utilities/workflow-signal-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { CoreUser } from '$lib/models/core-user'; 2 | import type { Settings } from '$lib/types/global'; 3 | 4 | export const workflowSignalEnabled = ( 5 | settings: Settings, 6 | coreUser: CoreUser, 7 | namespace: string, 8 | ): boolean => { 9 | return ( 10 | !settings.disableWriteActions && 11 | !settings.workflowSignalDisabled && 12 | !coreUser.namespaceWriteDisabled(namespace) 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/utilities/workflow-terminate-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { CoreUser } from '$lib/models/core-user'; 2 | import type { Settings } from '$lib/types/global'; 3 | 4 | export const workflowTerminateEnabled = ( 5 | settings: Settings, 6 | coreUser: CoreUser, 7 | namespace: string, 8 | ): boolean => { 9 | return ( 10 | !settings.disableWriteActions && 11 | !settings.workflowTerminateDisabled && 12 | !coreUser.namespaceWriteDisabled(namespace) 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/utilities/workflow-update-enabled.ts: -------------------------------------------------------------------------------- 1 | import type { CoreUser } from '$lib/models/core-user'; 2 | import type { Settings } from '$lib/types/global'; 3 | 4 | export const workflowUpdateEnabled = ( 5 | settings: Settings, 6 | coreUser: CoreUser, 7 | namespace: string, 8 | ): boolean => { 9 | return ( 10 | !settings.disableWriteActions && 11 | !settings.workflowUpdateDisabled && 12 | !coreUser.namespaceWriteDisabled(namespace) 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/utilities/write-actions-are-allowed.ts: -------------------------------------------------------------------------------- 1 | import { get } from 'svelte/store'; 2 | 3 | import { settings } from '$lib/stores/settings'; 4 | 5 | export const writeActionsAreAllowed = (store = settings): boolean => { 6 | const isDisabled = get(store).disableWriteActions; 7 | return !isDisabled; 8 | }; 9 | -------------------------------------------------------------------------------- /src/lib/vendor/Temporal_Logo_Animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/Temporal_Logo_Animation.gif -------------------------------------------------------------------------------- /src/lib/vendor/Temporal_Logo_Animation.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/Temporal_Logo_Animation.mp4 -------------------------------------------------------------------------------- /src/lib/vendor/Temporal_Logo_Animation.webm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/Temporal_Logo_Animation.webm -------------------------------------------------------------------------------- /src/lib/vendor/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/android-chrome-192x192.png -------------------------------------------------------------------------------- /src/lib/vendor/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/android-chrome-512x512.png -------------------------------------------------------------------------------- /src/lib/vendor/andromeda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/andromeda.png -------------------------------------------------------------------------------- /src/lib/vendor/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/apple-touch-icon.png -------------------------------------------------------------------------------- /src/lib/vendor/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/banner.png -------------------------------------------------------------------------------- /src/lib/vendor/empty-state-dark_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/empty-state-dark_2x.png -------------------------------------------------------------------------------- /src/lib/vendor/empty-state-dark_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/empty-state-dark_4x.png -------------------------------------------------------------------------------- /src/lib/vendor/empty-state-light_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/empty-state-light_2x.png -------------------------------------------------------------------------------- /src/lib/vendor/empty-state-light_4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/empty-state-light_4x.png -------------------------------------------------------------------------------- /src/lib/vendor/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/favicon-16x16.png -------------------------------------------------------------------------------- /src/lib/vendor/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/favicon-32x32.png -------------------------------------------------------------------------------- /src/lib/vendor/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/favicon.ico -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/dot-net-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/dot-net-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/go-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/go-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/java-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/java-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/php-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/php-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/python-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/python-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/ruby-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/ruby-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/rust-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/rust-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/sdk-logos/ts-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/lib/vendor/sdk-logos/ts-logo.png -------------------------------------------------------------------------------- /src/lib/vendor/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Temporal Web UI", 3 | "short_name": "Temporal", 4 | "icons": [ 5 | { 6 | "src": "./android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ffffff", 17 | "background_color": "#ffffff", 18 | "display": "standalone" 19 | } 20 | -------------------------------------------------------------------------------- /src/routes/(app)/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForWorkflows } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async ({ parent }) => { 8 | const data = await parent(); 9 | 10 | const defaultNamespace = data?.settings?.defaultNamespace; 11 | const isCloud = data.settings.runtimeEnvironment?.isCloud; 12 | 13 | if (isCloud) { 14 | redirect( 15 | 302, 16 | routeForWorkflows({ 17 | namespace: defaultNamespace, 18 | }), 19 | ); 20 | } 21 | 22 | return { defaultNamespace }; 23 | }; 24 | -------------------------------------------------------------------------------- /src/routes/(app)/import/+layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/routes/(app)/import/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForEventHistoryImport } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async () => { 8 | const redirectPath = routeForEventHistoryImport(); 9 | 10 | redirect(302, redirectPath); 11 | }; 12 | -------------------------------------------------------------------------------- /src/routes/(app)/import/events/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/(app)/import/events/[namespace]/[workflow]/[run]/history/+layout.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/routes/(app)/import/events/[namespace]/[workflow]/[run]/history/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForEventHistoryImport } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async function () { 8 | const redirectPath = routeForEventHistoryImport('feed'); 9 | redirect(302, redirectPath); 10 | }; 11 | -------------------------------------------------------------------------------- /src/routes/(app)/import/events/[namespace]/[workflow]/[run]/history/json/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/+layout.ts: -------------------------------------------------------------------------------- 1 | import type { LayoutData, LayoutLoad } from './$types'; 2 | 3 | import { fetchSearchAttributesForNamespace } from '$lib/services/search-attributes-service'; 4 | import { allSearchAttributes } from '$lib/stores/search-attributes'; 5 | 6 | export const load: LayoutLoad = async ({ 7 | params, 8 | parent, 9 | fetch, 10 | }): Promise => { 11 | await parent(); 12 | const attributes = await fetchSearchAttributesForNamespace( 13 | params.namespace, 14 | fetch, 15 | ); 16 | 17 | allSearchAttributes.set(attributes); 18 | }; 19 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types'; 2 | 3 | import { fetchNamespace } from '$lib/services/namespaces-service'; 4 | import { getClusters } from '$lib/utilities/get-clusters'; 5 | 6 | export const load: PageLoad = async function ({ params, parent, url }) { 7 | const { searchParams } = url; 8 | 9 | if (searchParams.has('time-range')) searchParams.delete('time-range'); 10 | 11 | await parent(); 12 | const namespace = await fetchNamespace(params.namespace); 13 | const clusters = getClusters(namespace); 14 | 15 | return { 16 | namespace, 17 | clusters, 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/batch-operations/+page.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/batch-operations/[jobId]/+page.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/schedules/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/schedules/[schedule]/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/schedules/[schedule]/edit/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/schedules/create/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/task-queues/+layout.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

6 | {translate('common.task-queue')} 7 |

8 | 9 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/task-queues/+page.svelte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/routes/(app)/namespaces/[namespace]/task-queues/+page.svelte -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/task-queues/[queue]/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/worker-deployments/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/worker-deployments/[deployment]/+layout.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |
11 | 12 |
13 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/worker-deployments/[deployment]/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/+page.ts: -------------------------------------------------------------------------------- 1 | export const ssr = false; 2 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/+page.ts: -------------------------------------------------------------------------------- 1 | import { error, redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { fetchWorkflowForRunId } from '$lib/services/workflow-service'; 6 | 7 | export const load: PageLoad = async function ({ url, params }) { 8 | const { namespace, workflow: workflowId } = params; 9 | const { runId } = await fetchWorkflowForRunId({ namespace, workflowId }); 10 | 11 | if (runId) { 12 | redirect(302, `${url.pathname}/${runId}`); 13 | } else { 14 | error(404); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/+layout.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/+page.svelte: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/+page.svelte -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | export const load: PageLoad = async function ({ url }) { 6 | redirect(302, `${url.pathname}/history`); 7 | }; 8 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/call-stack/+page.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history.json/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { getEndpointForRawHistory } from '$lib/services/events-service'; 6 | 7 | export const load: PageLoad = async function ({ parent, params }) { 8 | await parent(); 9 | 10 | const { namespace, workflow, run } = params; 11 | const route = getEndpointForRawHistory({ 12 | namespace, 13 | workflowId: workflow, 14 | runId: run, 15 | }); 16 | redirect(302, route); 17 | }; 18 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/compact/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForEventHistory } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async function ({ params }) { 8 | const { namespace, workflow, run } = params; 9 | const route = routeForEventHistory({ 10 | namespace, 11 | workflow, 12 | run, 13 | }); 14 | redirect(302, route); 15 | }; 16 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/events/[id]/+page.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/feed/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForEventHistory } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async function ({ params }) { 8 | const { namespace, workflow, run } = params; 9 | const route = routeForEventHistory({ 10 | namespace, 11 | workflow, 12 | run, 13 | }); 14 | redirect(302, route); 15 | }; 16 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/history/json/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForEventHistory } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async function ({ params }) { 8 | const { namespace, workflow, run } = params; 9 | const route = routeForEventHistory({ 10 | namespace, 11 | workflow, 12 | run, 13 | }); 14 | redirect(302, route); 15 | }; 16 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/metadata/+page.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/pending-activities/+page.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/query/+page.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/relationships/+page.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/[workflow]/[run]/stack-trace/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForCallStack } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async function ({ params }) { 8 | redirect(302, routeForCallStack(params)); 9 | }; 10 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/start-workflow/+page.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/routes/(app)/namespaces/[namespace]/workflows/start-workflow/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | import type { PageLoad } from './$types'; 4 | 5 | import { routeForWorkflows } from '$lib/utilities/route-for'; 6 | 7 | export const load: PageLoad = async function ({ params, parent }) { 8 | const data = await parent(); 9 | const disabled = 10 | data?.settings?.disableWriteActions || 11 | data?.settings?.startWorkflowDisabled; 12 | 13 | if (disabled) { 14 | const { namespace } = params; 15 | redirect(302, routeForWorkflows({ namespace })); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/routes/(app)/nexus/+layout.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 |
7 |

8 | Sorry, this feature is not available in this version of Temporal. Please 9 | enable Nexus or upgrade to a newer version. 10 |

11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /src/routes/(app)/nexus/+page.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/routes/(app)/nexus/+page.ts: -------------------------------------------------------------------------------- 1 | import { fetchNexusEndpoints } from '$lib/services/nexus-service.js'; 2 | 3 | import type { PageLoad } from '../$types'; 4 | 5 | export const load: PageLoad = async ({ fetch, url }) => { 6 | const search = url.searchParams.get('search') || ''; 7 | const endpoints = await fetchNexusEndpoints(search, fetch); 8 | return { 9 | endpoints, 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /src/routes/(app)/nexus/[id]/+layout.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#if !endpoint} 13 | 20 | {:else} 21 | 22 | {/if} 23 | -------------------------------------------------------------------------------- /src/routes/(app)/nexus/[id]/+page.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /src/routes/(login)/+layout.svelte: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/routes/(login)/+layout.ts: -------------------------------------------------------------------------------- 1 | import '../../app.css'; 2 | 3 | import { error } from '@sveltejs/kit'; 4 | 5 | import type { LayoutLoad } from './$types'; 6 | 7 | import { fetchSettings } from '$lib/services/settings-service'; 8 | import type { Settings } from '$lib/types/global'; 9 | 10 | export const ssr = false; 11 | 12 | export const load: LayoutLoad = async function ({ fetch }) { 13 | const settings: Settings = await fetchSettings(fetch); 14 | 15 | if (!settings.auth.enabled) { 16 | error(404); 17 | } 18 | 19 | return { 20 | settings, 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | import i18next from 'i18next'; 2 | import LanguageDetector from 'i18next-browser-languagedetector'; 3 | 4 | import type { LayoutData, LayoutLoad } from './$types'; 5 | 6 | import { i18nNamespaces } from '$lib/i18n'; 7 | import resources from '$lib/i18n/locales'; 8 | 9 | export const ssr = false; 10 | 11 | export const load: LayoutLoad = async function (): LayoutData { 12 | i18next.use(LanguageDetector).init({ 13 | fallbackLng: 'en', 14 | load: 'languageOnly', 15 | ns: i18nNamespaces, 16 | defaultNS: 'common', 17 | detection: { 18 | order: ['querystring', 'localStorage', 'navigator'], 19 | caches: ['localStorage'], 20 | lookupQuerystring: 'lng', 21 | lookupLocalStorage: 'locale', 22 | }, 23 | resources, 24 | }); 25 | }; 26 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/ui/48ac8fe4f222c5af9b8663047cae46adfccd379e/static/favicon.ico -------------------------------------------------------------------------------- /tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss'; 2 | import { fontFamily } from 'tailwindcss/defaultTheme'; 3 | 4 | import temporal, { textStyles } from './src/lib/theme/plugin'; 5 | 6 | const config = { 7 | content: ['./src/**/*.{html,js,svelte,ts}'], 8 | darkMode: ['selector', '[data-theme="dark"]'], 9 | theme: { 10 | fontFamily: { 11 | sans: ['Inter', ...fontFamily.sans], 12 | mono: ['Noto Sans Mono', ...fontFamily.mono], 13 | }, 14 | extend: { 15 | animation: { 16 | 'spin-slow': 'spin 3s linear infinite', 17 | }, 18 | zIndex: { 19 | 100: '100', 20 | }, 21 | }, 22 | }, 23 | plugins: [temporal, textStyles], 24 | } satisfies Config; 25 | 26 | export default config; 27 | -------------------------------------------------------------------------------- /temporal/activities/double.ts: -------------------------------------------------------------------------------- 1 | export default async function (value: number): Promise { 2 | return value * 2; 3 | } 4 | -------------------------------------------------------------------------------- /temporal/activities/echo.ts: -------------------------------------------------------------------------------- 1 | export default async function (input: string): Promise { 2 | return `Received ${input}`; 3 | } 4 | -------------------------------------------------------------------------------- /temporal/activities/index.ts: -------------------------------------------------------------------------------- 1 | export { default as echo } from './echo'; 2 | export { default as double } from './double'; 3 | -------------------------------------------------------------------------------- /temporal/data-converter.ts: -------------------------------------------------------------------------------- 1 | import type { DataConverter } from '@temporalio/common'; 2 | 3 | import { PayloadCodec } from './payload-codec'; 4 | 5 | let dataConverter: DataConverter; 6 | 7 | export function getDataConverter(): DataConverter { 8 | if (!dataConverter) { 9 | dataConverter = createDataConverter(); 10 | } 11 | return dataConverter; 12 | } 13 | 14 | function createDataConverter(): DataConverter { 15 | return { 16 | payloadCodecs: [new PayloadCodec()], 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /tests/e2e/storageState.json: -------------------------------------------------------------------------------- 1 | { 2 | "cookies": [], 3 | "origins": [] 4 | } -------------------------------------------------------------------------------- /tests/global-teardown.ts: -------------------------------------------------------------------------------- 1 | import { FullConfig } from '@playwright/test'; 2 | 3 | import { disconnect, stopWorkflows } from '../temporal/client'; 4 | import { getCodecServer } from '../temporal/codec-server'; 5 | import { stopWorker } from '../temporal/workers'; 6 | import { getTemporalServer } from '../utilities/temporal-server'; 7 | import { getUIServer } from '../utilities/ui-server'; 8 | 9 | export default async function (config: FullConfig) { 10 | if (config.metadata.mode === 'e2e') { 11 | const temporal = getTemporalServer(); 12 | const codecServer = getCodecServer(); 13 | const uiServer = getUIServer(); 14 | 15 | await stopWorkflows(); 16 | await stopWorker(); 17 | await disconnect(); 18 | await codecServer.stop(); 19 | await uiServer.shutdown(); 20 | await temporal.shutdown(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/integration/storageState.json: -------------------------------------------------------------------------------- 1 | { 2 | "cookies": [], 3 | "origins": [] 4 | } -------------------------------------------------------------------------------- /tests/test-utilities/accessibility-reporter/unique.ts: -------------------------------------------------------------------------------- 1 | export const unique = (items: T[]) => 2 | items.reduce((acc: T[], current: T) => { 3 | const x = acc.find((item) => item.id === current.id); 4 | if (!x) { 5 | return acc.concat([current]); 6 | } else { 7 | return acc; 8 | } 9 | }, []); 10 | -------------------------------------------------------------------------------- /tests/test-utilities/custom-matchers.ts: -------------------------------------------------------------------------------- 1 | import { expect } from '@playwright/test'; 2 | 3 | expect.extend({ 4 | async toHaveLocalStorageItem(page, key: string, expected: string) { 5 | const stored = await page.evaluate((key) => localStorage.getItem(key), key); 6 | const pass = stored === JSON.stringify(expected); 7 | return { 8 | pass, 9 | message: () => 10 | pass 11 | ? `Expected local storage at key "${key}" not to have value "${JSON.stringify(expected)}"` 12 | : `Expected local storage at key "${key}" to have value ${JSON.stringify(expected)}, but got ${stored}`, 13 | }; 14 | }, 15 | }); 16 | 17 | declare module '@playwright/test' { 18 | interface Matchers { 19 | toHaveLocalStorageItem(key: string, expected: string): Promise; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/test-utilities/mock-date.ts: -------------------------------------------------------------------------------- 1 | export const mockDate = () => { 2 | const _Date = window.Date; 3 | 4 | class FakeDate extends _Date { 5 | constructor( 6 | date: 7 | | number 8 | | string 9 | | Date = 'Wed Sept 19 2012 12:00:00 GMT-0600 (Mountain Daylight Time)', 10 | ) { 11 | super(); 12 | return new _Date(date); 13 | } 14 | } 15 | 16 | window.Date = FakeDate as DateConstructor; 17 | }; 18 | -------------------------------------------------------------------------------- /tests/test-utilities/mock-local-storage.ts: -------------------------------------------------------------------------------- 1 | import type { Page } from '@playwright/test'; 2 | 3 | export const setLocalStorage = async ( 4 | key: string, 5 | value: string, 6 | page: Page, 7 | ) => { 8 | await page.addInitScript( 9 | (item) => { 10 | window.localStorage.setItem(item.key, item.value); 11 | }, 12 | { key, value }, 13 | ); 14 | await page.reload(); 15 | }; 16 | 17 | export const removeLocalStorageItem = async ( 18 | key: string, 19 | page: Page, 20 | ): Promise => { 21 | await page.addInitScript((key) => window.localStorage.removeItem(key), key); 22 | await page.reload(); 23 | }; 24 | -------------------------------------------------------------------------------- /tests/test-utilities/mocks/batch-operations.ts: -------------------------------------------------------------------------------- 1 | import { Page } from '@playwright/test'; 2 | 3 | export const CREATE_BATCH_OPERATION_API = 4 | '**/api/v1/namespaces/*/batch-operations*'; 5 | 6 | export const DESCRIBE_BATCH_OPERATION_API = 7 | '**/api/v1/namespaces/*/batch-operations/*'; 8 | 9 | export const mockCreateBatchOperationApi = (page: Page) => { 10 | return page.route(CREATE_BATCH_OPERATION_API, (route) => { 11 | route.fulfill({ json: {} }); 12 | }); 13 | }; 14 | 15 | export const mockDescribeBatchOperationApi = (page: Page) => { 16 | return page.route(DESCRIBE_BATCH_OPERATION_API, (route) => { 17 | route.fulfill({ json: { state: 'COMPLETED', completeOperationCount: 10 } }); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /tests/test-utilities/mocks/query.ts: -------------------------------------------------------------------------------- 1 | import { Page } from '@playwright/test'; 2 | 3 | const json = { 4 | queryResult: { 5 | payloads: [ 6 | { 7 | an: 'error', 8 | }, 9 | ], 10 | }, 11 | queryRejected: null, 12 | }; 13 | 14 | export default async function mockQueryApiWithStackTraceError(page: Page) { 15 | await page.route( 16 | '**/api/v1/namespaces/default/workflows/*/runs/*/query*', 17 | async (route) => { 18 | route.fulfill({ json }); 19 | }, 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /tests/test-utilities/mocks/task-queues.ts: -------------------------------------------------------------------------------- 1 | import type { Page } from '@playwright/test'; 2 | 3 | export const TASK_QUEUES_API = '**/api/v1/namespaces/*/task-queues/*?*'; 4 | 5 | const mockTaskQueues = { 6 | pollers: [ 7 | { 8 | lastAccessTime: '2022-05-05T21:42:46.576609378Z', 9 | identity: '@poller', 10 | ratePerSecond: 100000, 11 | }, 12 | ], 13 | taskQueueStatus: null, 14 | }; 15 | 16 | export const mockTaskQueuesApi = (page: Page) => { 17 | return page.route(TASK_QUEUES_API, (route) => { 18 | return route.fulfill({ json: mockTaskQueues }); 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /tests/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "$src/*": ["../src/*"], 6 | "~/*": ["./*"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [ 3 | { 4 | "source": "/(.*)", 5 | "destination": "/" 6 | } 7 | ] 8 | } 9 | --------------------------------------------------------------------------------