@shortlink-org/portolan 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -205
- package/cli/init.mjs +285 -0
- package/cli/init.test.mjs +158 -0
- package/cli/portolan.mjs +37 -73
- package/index.html +19 -0
- package/package.json +5 -10
- package/plugins/README.md +66 -27
- package/plugins/portolan-go.wasm +0 -0
- package/portolan.json +52 -73
- package/public/landing/cats/bug-hunter.webp +0 -0
- package/public/landing/cats/compass-nap.webp +0 -0
- package/public/landing/cats/diagrammer.webp +0 -0
- package/public/landing/cats/docs-reader.webp +0 -0
- package/public/landing/cats/laptop.webp +0 -0
- package/public/landing/cats/map-inspector.webp +0 -0
- package/public/landing/cats/server-break.webp +0 -0
- package/public/landing/cats/star-mapper.webp +0 -0
- package/public/landing/cats/system-builder.webp +0 -0
- package/public/landing/cats/thread-tangle.webp +0 -0
- package/public/og.png +0 -0
- package/public/readme-header.webp +0 -0
- package/schema/portolan.schema.json +180 -0
- package/scripts/builtin-plugins.mjs +14 -0
- package/scripts/delivery-presets.mjs +353 -0
- package/scripts/gen.mjs +37 -4
- package/scripts/history.mjs +126 -0
- package/scripts/history.test.mjs +110 -0
- package/scripts/host-plugins/fetch-bsr.mjs +351 -0
- package/{plugins/fetch-bsr/options.schema.json → scripts/host-plugins/fetch-bsr.options.json} +4 -0
- package/scripts/host-plugins/fetch-bsr.test.mjs +212 -0
- package/scripts/host-plugins/fetch-csr.mjs +386 -0
- package/scripts/host-plugins/fetch-csr.test.mjs +178 -0
- package/scripts/host-plugins/fetch-git.mjs +379 -0
- package/scripts/host-plugins/fetch-git.test.mjs +210 -0
- package/scripts/local-api.mjs +22 -4
- package/scripts/local-api.test.mjs +84 -2
- package/scripts/manifest.mjs +3 -2
- package/scripts/package-smoke.mjs +6 -2
- package/scripts/plugin-host.mjs +77 -18
- package/scripts/plugin-host.test.mjs +50 -1
- package/scripts/plugin-wasm-worker.mjs +6 -3
- package/scripts/run-builtin.mjs +6 -26
- package/scripts/schema.mjs +6 -1
- package/src/app/App.tsx +24 -429
- package/src/app/Breadcrumbs.test.ts +7 -0
- package/src/app/Breadcrumbs.tsx +15 -2
- package/src/app/CatalogApp.tsx +436 -0
- package/src/app/Sidebar.tsx +0 -1
- package/src/chat/ChatPanel.tsx +98 -41
- package/src/chat/Composer.tsx +4 -2
- package/src/chat/Conversation.tsx +79 -15
- package/src/chat/Header.tsx +43 -9
- package/src/chat/Starter.tsx +64 -3
- package/src/chat/Steps.tsx +1 -1
- package/src/chat/flags.test.ts +51 -14
- package/src/chat/flags.ts +8 -2
- package/src/chat/page-context.test.ts +51 -0
- package/src/chat/page-context.ts +128 -0
- package/src/chat/prompt.test.ts +49 -1
- package/src/chat/prompt.ts +42 -1
- package/src/chat/tools.ts +7 -2
- package/src/chat/transport.ts +16 -4
- package/src/components/ProblemRow.tsx +182 -0
- package/src/data.ts +20 -5
- package/src/er/ErCanvas.tsx +1 -1
- package/src/graph/DependencyGraph.tsx +10 -1
- package/src/graph/FocusedEventGraph.tsx +1 -1
- package/src/graph/GraphToolbar.tsx +2 -4
- package/src/index.css +220 -2
- package/src/landing/DraggableReveal.tsx +168 -0
- package/src/landing/EstateGraph.tsx +28 -0
- package/src/landing/FlowPlayback.tsx +282 -0
- package/src/landing/HeroMap.tsx +128 -0
- package/src/landing/LandingPage.tsx +757 -0
- package/src/landing/ProductFrame.tsx +68 -0
- package/src/landing/ProductTour.tsx +371 -0
- package/src/landing/catalog.ts +25 -0
- package/src/landing/motion.tsx +52 -0
- package/src/lib/all-problems.ts +28 -0
- package/src/lib/local-api.ts +35 -1
- package/src/lib/setup-info.test.ts +8 -0
- package/src/lib/setup-info.ts +3 -2
- package/src/lib/source-code.ts +2 -1
- package/src/likec4/C4View.tsx +5 -0
- package/src/likec4/InteractiveView.tsx +5 -1
- package/src/map/ContextMapGraph.tsx +47 -20
- package/src/pages/ContextMap.tsx +5 -1
- package/src/pages/FlowDetail.tsx +24 -7
- package/src/pages/Overview.tsx +68 -12
- package/src/pages/Problems.tsx +6 -193
- package/src/pages/Settings.tsx +133 -108
- package/src/pages/settings/DeliverySettings.tsx +229 -0
- package/src/pages/settings/PreferencesSettings.tsx +101 -0
- package/src/routes.test.ts +20 -0
- package/src/routes.ts +13 -1
- package/vite.config.ts +12 -8
- package/catalog/enum_test.go +0 -46
- package/catalog/model.go +0 -932
- package/catalog/roundtrip_test.go +0 -185
- package/catalog/via_test.go +0 -38
- package/go.mod +0 -14
- package/go.sum +0 -14
- package/internal/commands/cargo.go +0 -154
- package/internal/commands/commands.go +0 -143
- package/internal/commands/commands_test.go +0 -323
- package/internal/commands/gradle.go +0 -77
- package/internal/commands/justfile.go +0 -82
- package/internal/commands/makefile.go +0 -106
- package/internal/commands/maven.go +0 -93
- package/internal/commands/packagejson.go +0 -159
- package/internal/commands/pyproject.go +0 -169
- package/internal/commands/taskfile.go +0 -101
- package/internal/commands/testdata/estate/.cargo/config.toml +0 -7
- package/internal/commands/testdata/estate/Makefile +0 -33
- package/internal/commands/testdata/estate/Taskfile.yml +0 -21
- package/internal/commands/testdata/estate/build.gradle.kts +0 -21
- package/internal/commands/testdata/estate/justfile +0 -20
- package/internal/commands/testdata/estate/package.json +0 -13
- package/internal/commands/testdata/estate/pom.xml +0 -29
- package/internal/commands/testdata/estate/pyproject.toml +0 -27
- package/internal/commands/testdata/estate/xtask/src/main.rs +0 -27
- package/internal/commands/testdata/golden/commands.json +0 -305
- package/internal/gohttp/analyze.go +0 -2500
- package/internal/gohttp/endpoints.go +0 -1067
- package/internal/gohttp/roots.go +0 -320
- package/internal/gohttp/typed.go +0 -143
- package/internal/goscan/constants.go +0 -85
- package/internal/goscan/goscan_test.go +0 -227
- package/internal/goscan/names.go +0 -52
- package/internal/goscan/parse_test.go +0 -11
- package/internal/goscan/source.go +0 -36
- package/internal/goscan/tree.go +0 -155
- package/internal/goscan/types.go +0 -99
- package/internal/wsdl/ids.go +0 -127
- package/internal/wsdl/ids_test.go +0 -21
- package/internal/wsdl/model.go +0 -62
- package/internal/wsdl/parse.go +0 -920
- package/internal/wsdl/parse_test.go +0 -133
- package/plugin/describe.go +0 -107
- package/plugin/describe_test.go +0 -114
- package/plugin/protocol.go +0 -117
- package/plugin/schematest/schematest.go +0 -126
- package/plugins/extract-adr/describe.go +0 -19
- package/plugins/extract-adr/describe_test.go +0 -11
- package/plugins/extract-adr/extract.go +0 -153
- package/plugins/extract-adr/extract_test.go +0 -350
- package/plugins/extract-adr/history.go +0 -99
- package/plugins/extract-adr/main.go +0 -65
- package/plugins/extract-adr/parse.go +0 -642
- package/plugins/extract-adr/parse_test.go +0 -405
- package/plugins/extract-asyncapi/describe.go +0 -19
- package/plugins/extract-asyncapi/describe_test.go +0 -11
- package/plugins/extract-asyncapi/extract.go +0 -315
- package/plugins/extract-asyncapi/extract_test.go +0 -197
- package/plugins/extract-asyncapi/main.go +0 -48
- package/plugins/extract-asyncapi/spec.go +0 -151
- package/plugins/extract-commands/describe.go +0 -19
- package/plugins/extract-commands/describe_test.go +0 -11
- package/plugins/extract-commands/extract.go +0 -79
- package/plugins/extract-commands/extract_test.go +0 -92
- package/plugins/extract-commands/main.go +0 -55
- package/plugins/extract-csr/avro.go +0 -258
- package/plugins/extract-csr/describe.go +0 -19
- package/plugins/extract-csr/describe_test.go +0 -11
- package/plugins/extract-csr/extract.go +0 -338
- package/plugins/extract-csr/extract_test.go +0 -374
- package/plugins/extract-csr/jsonschema.go +0 -343
- package/plugins/extract-csr/lock.go +0 -25
- package/plugins/extract-csr/main.go +0 -84
- package/plugins/extract-csr/subject.go +0 -102
- package/plugins/extract-flows/describe.go +0 -19
- package/plugins/extract-flows/describe_test.go +0 -11
- package/plugins/extract-flows/extract.go +0 -91
- package/plugins/extract-flows/main.go +0 -45
- package/plugins/extract-flows/parse.go +0 -593
- package/plugins/extract-flows/parse_test.go +0 -204
- package/plugins/extract-glossary/describe.go +0 -19
- package/plugins/extract-glossary/describe_test.go +0 -11
- package/plugins/extract-glossary/extract.go +0 -115
- package/plugins/extract-glossary/extract_test.go +0 -220
- package/plugins/extract-glossary/main.go +0 -59
- package/plugins/extract-glossary/parse.go +0 -214
- package/plugins/extract-glossary/parse_test.go +0 -203
- package/plugins/extract-go/aggregate.go +0 -214
- package/plugins/extract-go/client.go +0 -409
- package/plugins/extract-go/client_test.go +0 -266
- package/plugins/extract-go/describe.go +0 -19
- package/plugins/extract-go/describe_test.go +0 -11
- package/plugins/extract-go/enum.go +0 -195
- package/plugins/extract-go/enum_test.go +0 -82
- package/plugins/extract-go/event.go +0 -99
- package/plugins/extract-go/extract.go +0 -191
- package/plugins/extract-go/extract_test.go +0 -261
- package/plugins/extract-go/flow.go +0 -1441
- package/plugins/extract-go/flow_test.go +0 -609
- package/plugins/extract-go/httpclient.go +0 -174
- package/plugins/extract-go/httpclient_test.go +0 -296
- package/plugins/extract-go/ids.go +0 -92
- package/plugins/extract-go/layout.go +0 -225
- package/plugins/extract-go/layout_test.go +0 -125
- package/plugins/extract-go/lifecycle.go +0 -324
- package/plugins/extract-go/lifecycle_test.go +0 -108
- package/plugins/extract-go/main.go +0 -79
- package/plugins/extract-go/operation.go +0 -157
- package/plugins/extract-go/source.go +0 -316
- package/plugins/extract-go/transport.go +0 -321
- package/plugins/extract-go/transport_test.go +0 -145
- package/plugins/extract-go/wiring.go +0 -434
- package/plugins/extract-go-nats/describe.go +0 -19
- package/plugins/extract-go-nats/describe_test.go +0 -11
- package/plugins/extract-go-nats/extract.go +0 -177
- package/plugins/extract-go-nats/extract_test.go +0 -389
- package/plugins/extract-go-nats/index.go +0 -379
- package/plugins/extract-go-nats/main.go +0 -42
- package/plugins/extract-go-nats/resolve.go +0 -161
- package/plugins/extract-go-nats/sites.go +0 -224
- package/plugins/extract-graphql/describe.go +0 -19
- package/plugins/extract-graphql/describe_test.go +0 -11
- package/plugins/extract-graphql/extract.go +0 -433
- package/plugins/extract-graphql/extract_test.go +0 -256
- package/plugins/extract-graphql/ids.go +0 -49
- package/plugins/extract-graphql/lex.go +0 -237
- package/plugins/extract-graphql/main.go +0 -51
- package/plugins/extract-graphql/parse.go +0 -621
- package/plugins/extract-graphql/parse_test.go +0 -122
- package/plugins/extract-http-clients/describe.go +0 -19
- package/plugins/extract-http-clients/describe_test.go +0 -11
- package/plugins/extract-http-clients/extract.go +0 -705
- package/plugins/extract-http-clients/extract_test.go +0 -1263
- package/plugins/extract-http-clients/main.go +0 -42
- package/plugins/extract-openapi/describe.go +0 -19
- package/plugins/extract-openapi/describe_test.go +0 -11
- package/plugins/extract-openapi/discover.go +0 -243
- package/plugins/extract-openapi/extract.go +0 -526
- package/plugins/extract-openapi/extract_test.go +0 -545
- package/plugins/extract-openapi/main.go +0 -75
- package/plugins/extract-openapi/spec.go +0 -350
- package/plugins/extract-project/describe.go +0 -19
- package/plugins/extract-project/describe_test.go +0 -11
- package/plugins/extract-project/extract.go +0 -221
- package/plugins/extract-project/extract_test.go +0 -109
- package/plugins/extract-project/main.go +0 -41
- package/plugins/extract-proto/ast.go +0 -125
- package/plugins/extract-proto/consumes.go +0 -77
- package/plugins/extract-proto/describe.go +0 -19
- package/plugins/extract-proto/extract.go +0 -293
- package/plugins/extract-proto/extract_test.go +0 -459
- package/plugins/extract-proto/ids.go +0 -89
- package/plugins/extract-proto/ids_test.go +0 -57
- package/plugins/extract-proto/lex.go +0 -285
- package/plugins/extract-proto/main.go +0 -100
- package/plugins/extract-proto/module.go +0 -120
- package/plugins/extract-proto/parse.go +0 -720
- package/plugins/extract-proto/parse_test.go +0 -307
- package/plugins/extract-proto/provides.go +0 -236
- package/plugins/extract-proto/resolve.go +0 -222
- package/plugins/extract-proto/resolve_test.go +0 -119
- package/plugins/extract-redis/describe.go +0 -19
- package/plugins/extract-redis/describe_test.go +0 -11
- package/plugins/extract-redis/extract.go +0 -183
- package/plugins/extract-redis/extract_test.go +0 -168
- package/plugins/extract-redis/keyspaces.go +0 -469
- package/plugins/extract-redis/main.go +0 -44
- package/plugins/extract-river/describe.go +0 -19
- package/plugins/extract-river/describe_test.go +0 -11
- package/plugins/extract-river/extract.go +0 -507
- package/plugins/extract-river/extract_test.go +0 -132
- package/plugins/extract-river/main.go +0 -42
- package/plugins/extract-sql/ddl.go +0 -893
- package/plugins/extract-sql/ddl_test.go +0 -401
- package/plugins/extract-sql/describe.go +0 -19
- package/plugins/extract-sql/describe_test.go +0 -11
- package/plugins/extract-sql/layout.go +0 -221
- package/plugins/extract-sql/layout_test.go +0 -55
- package/plugins/extract-sql/lineage.go +0 -107
- package/plugins/extract-sql/main.go +0 -141
- package/plugins/extract-sql/maps.go +0 -564
- package/plugins/extract-sql/maps_java.go +0 -117
- package/plugins/extract-sql/maps_rust.go +0 -333
- package/plugins/extract-sql/maps_rust_test.go +0 -70
- package/plugins/extract-sql/maps_test.go +0 -204
- package/plugins/extract-sql/maps_ts.go +0 -398
- package/plugins/extract-sql/maps_ts_test.go +0 -136
- package/plugins/extract-sql/projection.go +0 -70
- package/plugins/extract-sql/projection_test.go +0 -50
- package/plugins/extract-sql/store.go +0 -420
- package/plugins/extract-sql/store_test.go +0 -233
- package/plugins/extract-sql/view.go +0 -295
- package/plugins/extract-watermill/describe.go +0 -19
- package/plugins/extract-watermill/describe_test.go +0 -11
- package/plugins/extract-watermill/extract.go +0 -1228
- package/plugins/extract-watermill/extract_test.go +0 -234
- package/plugins/extract-watermill/main.go +0 -41
- package/plugins/extract-wsdl/describe.go +0 -19
- package/plugins/extract-wsdl/describe_test.go +0 -11
- package/plugins/extract-wsdl/extract.go +0 -148
- package/plugins/extract-wsdl/extract_test.go +0 -45
- package/plugins/extract-wsdl/main.go +0 -53
- package/plugins/fetch-bsr/auth.go +0 -114
- package/plugins/fetch-bsr/bsr.go +0 -240
- package/plugins/fetch-bsr/cache.go +0 -66
- package/plugins/fetch-bsr/describe.go +0 -19
- package/plugins/fetch-bsr/fetch.go +0 -192
- package/plugins/fetch-bsr/fetch_test.go +0 -465
- package/plugins/fetch-bsr/lock.go +0 -71
- package/plugins/fetch-bsr/main.go +0 -117
- package/plugins/fetch-csr/auth.go +0 -95
- package/plugins/fetch-csr/cache.go +0 -69
- package/plugins/fetch-csr/describe.go +0 -19
- package/plugins/fetch-csr/describe_test.go +0 -11
- package/plugins/fetch-csr/fetch.go +0 -254
- package/plugins/fetch-csr/fetch_test.go +0 -494
- package/plugins/fetch-csr/lock.go +0 -95
- package/plugins/fetch-csr/main.go +0 -122
- package/plugins/fetch-csr/registry.go +0 -209
- package/plugins/fetch-git/cache.go +0 -65
- package/plugins/fetch-git/describe.go +0 -19
- package/plugins/fetch-git/describe_test.go +0 -11
- package/plugins/fetch-git/fetch.go +0 -181
- package/plugins/fetch-git/fetch_test.go +0 -332
- package/plugins/fetch-git/git.go +0 -169
- package/plugins/fetch-git/lock.go +0 -72
- package/plugins/fetch-git/main.go +0 -127
- package/plugins/fetch-git/offline.go +0 -39
- package/plugins/fetch-git/pin.go +0 -90
- package/plugins/fetch-git/pin_test.go +0 -74
- package/plugins/gen-backstage/describe.go +0 -17
- package/plugins/gen-backstage/main.go +0 -22
- package/plugins/gen-backstage/plugin.go +0 -473
- package/plugins/gen-backstage/plugin_test.go +0 -145
- package/plugins/gen-backstage.wasm +0 -0
- package/plugins/gen-markdown/adr.go +0 -162
- package/plugins/gen-markdown/aggregate.go +0 -362
- package/plugins/gen-markdown/canonical.go +0 -93
- package/plugins/gen-markdown/context.go +0 -90
- package/plugins/gen-markdown/coverage_test.go +0 -89
- package/plugins/gen-markdown/describe.go +0 -19
- package/plugins/gen-markdown/describe_test.go +0 -11
- package/plugins/gen-markdown/external.go +0 -71
- package/plugins/gen-markdown/flow.go +0 -277
- package/plugins/gen-markdown/glossary.go +0 -76
- package/plugins/gen-markdown/glossary_test.go +0 -148
- package/plugins/gen-markdown/llms.go +0 -302
- package/plugins/gen-markdown/main.go +0 -22
- package/plugins/gen-markdown/markdown.go +0 -254
- package/plugins/gen-markdown/markdown_test.go +0 -100
- package/plugins/gen-markdown/module.go +0 -107
- package/plugins/gen-markdown/plugin.go +0 -39
- package/plugins/gen-markdown/quality_test.go +0 -176
- package/plugins/gen-markdown/redis_test.go +0 -38
- package/plugins/gen-markdown/render.go +0 -344
- package/plugins/gen-markdown/render_test.go +0 -186
- package/plugins/gen-markdown/service.go +0 -456
- package/plugins/gen-markdown/source.go +0 -177
- package/plugins/gen-markdown/store.go +0 -201
- package/plugins/gen-markdown.wasm +0 -0
- package/plugins/gen-mermaid/describe.go +0 -17
- package/plugins/gen-mermaid/main.go +0 -22
- package/plugins/gen-mermaid/plugin.go +0 -104
- package/plugins/gen-mermaid/plugin_test.go +0 -33
- package/plugins/gen-mermaid.wasm +0 -0
- package/plugins/openapi/ids.go +0 -261
- package/plugins/openapi/ids_test.go +0 -98
- package/plugins/verify-codeowners/describe.go +0 -19
- package/plugins/verify-codeowners/describe_test.go +0 -11
- package/plugins/verify-codeowners/main.go +0 -75
- package/plugins/verify-codeowners/match.go +0 -85
- package/plugins/verify-codeowners/match_test.go +0 -47
- package/plugins/verify-codeowners/owners.go +0 -164
- package/plugins/verify-codeowners/owners_test.go +0 -225
- package/plugins/verify-codeowners/parse.go +0 -90
- package/plugins/verify-codeowners/parse_test.go +0 -62
- package/plugins/verify-otel/describe.go +0 -19
- package/plugins/verify-otel/describe_test.go +0 -11
- package/plugins/verify-otel/main.go +0 -53
- package/plugins/verify-otel/match.go +0 -336
- package/plugins/verify-otel/otlp.go +0 -200
- package/plugins/verify-otel/verify.go +0 -734
- package/plugins/verify-otel/verify_test.go +0 -460
- /package/{plugins/fetch-csr/options.schema.json → scripts/host-plugins/fetch-csr.options.json} +0 -0
- /package/{plugins/fetch-git/options.schema.json → scripts/host-plugins/fetch-git.options.json} +0 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { CompassRose } from "../components/logo";
|
|
3
|
+
|
|
4
|
+
export function ProductFrame({
|
|
5
|
+
title,
|
|
6
|
+
eyebrow,
|
|
7
|
+
children,
|
|
8
|
+
className = "",
|
|
9
|
+
aside = true,
|
|
10
|
+
}: {
|
|
11
|
+
title: string;
|
|
12
|
+
eyebrow: string;
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
className?: string;
|
|
15
|
+
/** The sketched catalog tree down the left. Off for a canvas that wants the whole width. */
|
|
16
|
+
aside?: boolean;
|
|
17
|
+
}) {
|
|
18
|
+
return (
|
|
19
|
+
<div
|
|
20
|
+
className={`overflow-hidden rounded-[18px] border border-line bg-canvas shadow-md ${className}`}
|
|
21
|
+
>
|
|
22
|
+
<div className="flex h-11 items-center gap-3 border-b border-line bg-surface px-4">
|
|
23
|
+
<div className="flex gap-1.5" aria-hidden>
|
|
24
|
+
<span className="size-2 rounded-full bg-unresolved/70" />
|
|
25
|
+
<span className="size-2 rounded-full bg-declared/70" />
|
|
26
|
+
<span className="size-2 rounded-full bg-verified/70" />
|
|
27
|
+
</div>
|
|
28
|
+
<div className="mono flex min-w-0 flex-1 items-center justify-center gap-1.5 text-faint">
|
|
29
|
+
<CompassRose size={13} />
|
|
30
|
+
<span className="truncate">{title}</span>
|
|
31
|
+
</div>
|
|
32
|
+
<span className="w-[35px]" aria-hidden />
|
|
33
|
+
</div>
|
|
34
|
+
<div
|
|
35
|
+
className={`grid min-h-0 grid-cols-1 ${
|
|
36
|
+
aside ? "sm:grid-cols-[132px_minmax(0,1fr)]" : ""
|
|
37
|
+
}`}
|
|
38
|
+
>
|
|
39
|
+
{aside ? (
|
|
40
|
+
<aside className="hidden border-r border-line bg-canvas p-3 sm:block">
|
|
41
|
+
<div className="label mb-3">{eyebrow}</div>
|
|
42
|
+
<div className="space-y-1.5" aria-hidden>
|
|
43
|
+
<div className="h-2 w-20 rounded-full bg-line-strong" />
|
|
44
|
+
<div className="h-2 w-16 rounded-full bg-line" />
|
|
45
|
+
<div className="h-2 w-24 rounded-full bg-line" />
|
|
46
|
+
</div>
|
|
47
|
+
<div className="label mt-6 mb-3">catalog</div>
|
|
48
|
+
<div className="space-y-2" aria-hidden>
|
|
49
|
+
<div className="flex items-center gap-2">
|
|
50
|
+
<span className="size-1.5 rounded-[1px] bg-[var(--ctx-0)]" />
|
|
51
|
+
<div className="h-2 w-16 rounded-full bg-line" />
|
|
52
|
+
</div>
|
|
53
|
+
<div className="flex items-center gap-2">
|
|
54
|
+
<span className="size-1.5 rounded-[1px] bg-[var(--ctx-1)]" />
|
|
55
|
+
<div className="h-2 w-20 rounded-full bg-line" />
|
|
56
|
+
</div>
|
|
57
|
+
<div className="flex items-center gap-2">
|
|
58
|
+
<span className="size-1.5 rounded-[1px] bg-[var(--ctx-2)]" />
|
|
59
|
+
<div className="h-2 w-14 rounded-full bg-line" />
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</aside>
|
|
63
|
+
) : null}
|
|
64
|
+
<div className="landing-grid min-w-0">{children}</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
// Four stops through the product, on the example estate. Two of them are the
|
|
2
|
+
// product's own canvases in a smaller box; the other two are read straight
|
|
3
|
+
// out of the same catalog. Nothing on this page is drawn from a mock-up.
|
|
4
|
+
|
|
5
|
+
import { lazy, Suspense, useMemo, useState } from "react";
|
|
6
|
+
import { Link } from "react-router";
|
|
7
|
+
import {
|
|
8
|
+
AlertTriangle,
|
|
9
|
+
ArrowRight,
|
|
10
|
+
Braces,
|
|
11
|
+
ExternalLink,
|
|
12
|
+
FileCode2,
|
|
13
|
+
GitBranch,
|
|
14
|
+
Network,
|
|
15
|
+
Radio,
|
|
16
|
+
} from "lucide-react";
|
|
17
|
+
import { catalog, index } from "../data";
|
|
18
|
+
import { DiagramSkeleton } from "../components/DiagramSkeleton";
|
|
19
|
+
import { contextVar } from "../lib/context-color";
|
|
20
|
+
import { allProblems } from "../lib/all-problems";
|
|
21
|
+
import {
|
|
22
|
+
AnimatePresence,
|
|
23
|
+
LayoutGroup,
|
|
24
|
+
m,
|
|
25
|
+
page,
|
|
26
|
+
staggerStyle,
|
|
27
|
+
transitions,
|
|
28
|
+
} from "../lib/motion";
|
|
29
|
+
import { eventPath, paths } from "../routes";
|
|
30
|
+
import { catalogTo } from "./catalog";
|
|
31
|
+
import { ProductFrame } from "./ProductFrame";
|
|
32
|
+
import { DraggableReveal } from "./DraggableReveal";
|
|
33
|
+
|
|
34
|
+
type DemoId = "estate" | "flow" | "contract" | "problems";
|
|
35
|
+
|
|
36
|
+
// The two canvases carry elk and React Flow; they arrive when their tab does.
|
|
37
|
+
const EstateGraph = lazy(() =>
|
|
38
|
+
import("./EstateGraph").then((mod) => ({ default: mod.EstateGraph })),
|
|
39
|
+
);
|
|
40
|
+
const FlowPlayback = lazy(() =>
|
|
41
|
+
import("./FlowPlayback").then((mod) => ({ default: mod.FlowPlayback })),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const tours: Array<{
|
|
45
|
+
id: DemoId;
|
|
46
|
+
number: string;
|
|
47
|
+
title: string;
|
|
48
|
+
copy: string;
|
|
49
|
+
icon: typeof Network;
|
|
50
|
+
}> = [
|
|
51
|
+
{
|
|
52
|
+
id: "estate",
|
|
53
|
+
number: "01",
|
|
54
|
+
title: "See the whole estate",
|
|
55
|
+
copy: "See every service and event between them. Switch views, focus a service, or export the map.",
|
|
56
|
+
icon: Network,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
id: "flow",
|
|
60
|
+
number: "02",
|
|
61
|
+
title: "Follow one real flow",
|
|
62
|
+
copy: "Follow a checkout across HTTP, gRPC, the database and the bus, one step at a time, with its source line.",
|
|
63
|
+
icon: GitBranch,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: "contract",
|
|
67
|
+
number: "03",
|
|
68
|
+
title: "Inspect the contract",
|
|
69
|
+
copy: "The event's payload, the channel it leaves on, who listens, and the file it was read from.",
|
|
70
|
+
icon: Braces,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "problems",
|
|
74
|
+
number: "04",
|
|
75
|
+
title: "Catch architecture drift",
|
|
76
|
+
copy: "Catch unresolved calls, second writers and unpublished channels during the merge, before review.",
|
|
77
|
+
icon: AlertTriangle,
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
const EVENT_ID = "shop.cart.basket.BasketCheckedOut";
|
|
82
|
+
|
|
83
|
+
function ContractDemo() {
|
|
84
|
+
const event = index.eventById.get(EVENT_ID);
|
|
85
|
+
const owner = index.eventOwner.get(EVENT_ID);
|
|
86
|
+
const version = event?.versions.at(-1);
|
|
87
|
+
const href = eventPath(EVENT_ID);
|
|
88
|
+
if (!event || !owner || !version) {
|
|
89
|
+
return (
|
|
90
|
+
<div className="p-6 text-sm text-muted">
|
|
91
|
+
The example catalog does not carry {EVENT_ID}.
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
const context = index.serviceContext.get(owner.service.id);
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div className="p-4 sm:p-6">
|
|
99
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
100
|
+
<div className="min-w-0">
|
|
101
|
+
<div className="mono text-event">domain event · {version.version}</div>
|
|
102
|
+
<div className="mt-1 text-md font-semibold text-ink">{event.name}</div>
|
|
103
|
+
<div className="mono mt-1 truncate text-muted">{event.id}</div>
|
|
104
|
+
</div>
|
|
105
|
+
{event.wire ? (
|
|
106
|
+
<span className="chip status-verified">
|
|
107
|
+
<Radio size={12} /> {event.wire.channel ?? event.wire.name}
|
|
108
|
+
</span>
|
|
109
|
+
) : null}
|
|
110
|
+
</div>
|
|
111
|
+
{version.doc ? (
|
|
112
|
+
<p className="mt-3 max-w-[560px] text-sm leading-6 text-muted">
|
|
113
|
+
{version.doc}
|
|
114
|
+
</p>
|
|
115
|
+
) : null}
|
|
116
|
+
<div className="mt-5 grid gap-4 lg:grid-cols-[minmax(0,1fr)_200px]">
|
|
117
|
+
<div className="overflow-hidden rounded-card border border-line bg-canvas">
|
|
118
|
+
<div className="label border-b border-line bg-surface px-3 py-2">
|
|
119
|
+
payload
|
|
120
|
+
</div>
|
|
121
|
+
{version.fields.map((field, i) => (
|
|
122
|
+
<div
|
|
123
|
+
key={field.name}
|
|
124
|
+
style={staggerStyle(i)}
|
|
125
|
+
className="stagger-in grid grid-cols-[minmax(0,1fr)_auto] gap-3 border-b border-line px-3 py-2 last:border-b-0"
|
|
126
|
+
>
|
|
127
|
+
<span className="mono text-ink">{field.name}</span>
|
|
128
|
+
<span className="mono text-muted">{field.type}</span>
|
|
129
|
+
</div>
|
|
130
|
+
))}
|
|
131
|
+
</div>
|
|
132
|
+
<div>
|
|
133
|
+
<div className="label mb-2">
|
|
134
|
+
published by
|
|
135
|
+
</div>
|
|
136
|
+
<div
|
|
137
|
+
className="rounded-control border border-line bg-canvas p-2.5"
|
|
138
|
+
style={{ color: contextVar(context?.id) }}
|
|
139
|
+
>
|
|
140
|
+
<div className="font-medium text-ink">{owner.service.id}</div>
|
|
141
|
+
<div className="mono mt-0.5 text-muted">{owner.aggregate.name}</div>
|
|
142
|
+
</div>
|
|
143
|
+
<div className="label mt-4 mb-2">
|
|
144
|
+
consumed by
|
|
145
|
+
</div>
|
|
146
|
+
{event.consumers.length === 0 ? (
|
|
147
|
+
<div className="mono text-faint">nobody yet</div>
|
|
148
|
+
) : (
|
|
149
|
+
<div className="space-y-2">
|
|
150
|
+
{event.consumers.map((consumer, i) => (
|
|
151
|
+
<div
|
|
152
|
+
key={consumer.service}
|
|
153
|
+
style={staggerStyle(i + 2)}
|
|
154
|
+
className="stagger-in flex items-center justify-between gap-2 rounded-control border border-line bg-canvas p-2.5"
|
|
155
|
+
>
|
|
156
|
+
<div className="min-w-0">
|
|
157
|
+
<div className="truncate font-medium text-ink">
|
|
158
|
+
{consumer.service}
|
|
159
|
+
</div>
|
|
160
|
+
{consumer.note ? (
|
|
161
|
+
<div className="mono mt-0.5 truncate text-muted">
|
|
162
|
+
{consumer.note}
|
|
163
|
+
</div>
|
|
164
|
+
) : null}
|
|
165
|
+
</div>
|
|
166
|
+
<span className={`chip status-${consumer.status}`}>
|
|
167
|
+
{consumer.status}
|
|
168
|
+
</span>
|
|
169
|
+
</div>
|
|
170
|
+
))}
|
|
171
|
+
</div>
|
|
172
|
+
)}
|
|
173
|
+
</div>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="mt-4 flex flex-wrap items-center gap-3">
|
|
176
|
+
<div className="mono flex min-w-0 flex-1 items-center gap-2 rounded-control border border-line bg-surface px-3 py-2 text-muted">
|
|
177
|
+
<FileCode2 size={13} className="shrink-0" />
|
|
178
|
+
<span className="truncate">{version.source}</span>
|
|
179
|
+
<ExternalLink size={12} className="ml-auto shrink-0" />
|
|
180
|
+
</div>
|
|
181
|
+
{href ? (
|
|
182
|
+
<Link
|
|
183
|
+
to={catalogTo(href)}
|
|
184
|
+
className="inline-flex shrink-0 items-center gap-1 text-sm text-accent hover:underline"
|
|
185
|
+
>
|
|
186
|
+
Open the event <ArrowRight size={13} />
|
|
187
|
+
</Link>
|
|
188
|
+
) : null}
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const SHOWN_PROBLEMS = 4;
|
|
195
|
+
|
|
196
|
+
function ProblemsDemo() {
|
|
197
|
+
const all = useMemo(() => allProblems(catalog, index), []);
|
|
198
|
+
const errors = all.filter((p) => p.severity === "error").length;
|
|
199
|
+
const shown = all.slice(0, SHOWN_PROBLEMS);
|
|
200
|
+
|
|
201
|
+
return (
|
|
202
|
+
<div className="p-4 sm:p-6">
|
|
203
|
+
<div className="mb-5 flex flex-wrap items-start justify-between gap-3">
|
|
204
|
+
<div>
|
|
205
|
+
<div className="text-md font-semibold text-ink">
|
|
206
|
+
Architecture problems
|
|
207
|
+
</div>
|
|
208
|
+
<div className="mt-1 text-sm text-muted">
|
|
209
|
+
Every edge that leaves the measured model, found at merge time
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
<div className="flex gap-2">
|
|
213
|
+
{errors > 0 ? (
|
|
214
|
+
<span className="chip status-unresolved">{errors} errors</span>
|
|
215
|
+
) : null}
|
|
216
|
+
{all.length - errors > 0 ? (
|
|
217
|
+
<span className="chip status-declared">
|
|
218
|
+
{all.length - errors} warnings
|
|
219
|
+
</span>
|
|
220
|
+
) : null}
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
{shown.length === 0 ? (
|
|
224
|
+
<div className="rounded-card border border-line bg-canvas p-4 text-sm text-muted">
|
|
225
|
+
Nothing leaves the model: every call and every consumer resolves.
|
|
226
|
+
</div>
|
|
227
|
+
) : (
|
|
228
|
+
<div className="space-y-2">
|
|
229
|
+
{shown.map((problem, i) => (
|
|
230
|
+
<div
|
|
231
|
+
key={`${problem.kind}:${problem.id}:${problem.peer}`}
|
|
232
|
+
style={staggerStyle(i)}
|
|
233
|
+
className="stagger-in grid grid-cols-[20px_minmax(0,1fr)_auto] items-start gap-3 rounded-card border border-line bg-canvas p-3"
|
|
234
|
+
>
|
|
235
|
+
<AlertTriangle
|
|
236
|
+
size={16}
|
|
237
|
+
className={
|
|
238
|
+
problem.severity === "error"
|
|
239
|
+
? "mt-0.5 text-unresolved"
|
|
240
|
+
: "mt-0.5 text-declared"
|
|
241
|
+
}
|
|
242
|
+
/>
|
|
243
|
+
<div className="min-w-0">
|
|
244
|
+
<div className="truncate font-medium text-ink">
|
|
245
|
+
{problem.note ?? problem.id}
|
|
246
|
+
</div>
|
|
247
|
+
<div
|
|
248
|
+
className="mono mt-1 truncate text-muted"
|
|
249
|
+
title={`${problem.service} → ${problem.peer}`}
|
|
250
|
+
>
|
|
251
|
+
<span style={{ color: contextVar(problem.context) }}>
|
|
252
|
+
{problem.service}
|
|
253
|
+
</span>{" "}
|
|
254
|
+
→ {problem.peer}
|
|
255
|
+
</div>
|
|
256
|
+
</div>
|
|
257
|
+
<span className="chip border-line text-muted">
|
|
258
|
+
{problem.kind}
|
|
259
|
+
</span>
|
|
260
|
+
</div>
|
|
261
|
+
))}
|
|
262
|
+
</div>
|
|
263
|
+
)}
|
|
264
|
+
<div className="mt-4 flex items-center justify-between gap-3">
|
|
265
|
+
<span className="mono text-faint">
|
|
266
|
+
{all.length > shown.length
|
|
267
|
+
? `${all.length - shown.length} more on the problems page`
|
|
268
|
+
: `${all.length} in total`}
|
|
269
|
+
</span>
|
|
270
|
+
<Link
|
|
271
|
+
to={catalogTo(paths.problems())}
|
|
272
|
+
className="inline-flex items-center gap-1 text-sm text-accent hover:underline"
|
|
273
|
+
>
|
|
274
|
+
Open the problems page <ArrowRight size={13} />
|
|
275
|
+
</Link>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function Demo({ id }: { id: DemoId }) {
|
|
282
|
+
switch (id) {
|
|
283
|
+
case "flow":
|
|
284
|
+
return <FlowPlayback />;
|
|
285
|
+
case "contract":
|
|
286
|
+
return <ContractDemo />;
|
|
287
|
+
case "problems":
|
|
288
|
+
return <ProblemsDemo />;
|
|
289
|
+
default:
|
|
290
|
+
return <EstateGraph />;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function ProductTour() {
|
|
295
|
+
const [active, setActive] = useState<DemoId>("estate");
|
|
296
|
+
const selected = tours.find((tour) => tour.id === active) ?? tours[0]!;
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div className="grid items-start gap-8 lg:grid-cols-[minmax(260px,0.72fr)_minmax(0,1.55fr)] lg:gap-12">
|
|
300
|
+
<LayoutGroup id="product-tour">
|
|
301
|
+
<div
|
|
302
|
+
role="tablist"
|
|
303
|
+
aria-label="Portolan product tour"
|
|
304
|
+
className="grid gap-2 sm:grid-cols-2 lg:grid-cols-1"
|
|
305
|
+
>
|
|
306
|
+
{tours.map((tour) => {
|
|
307
|
+
const Icon = tour.icon;
|
|
308
|
+
const on = tour.id === active;
|
|
309
|
+
return (
|
|
310
|
+
<button
|
|
311
|
+
key={tour.id}
|
|
312
|
+
type="button"
|
|
313
|
+
role="tab"
|
|
314
|
+
aria-selected={on}
|
|
315
|
+
aria-controls="landing-product-demo"
|
|
316
|
+
onClick={() => setActive(tour.id)}
|
|
317
|
+
className={`group relative rounded-card border border-line/60 p-4 text-left transition-colors ${
|
|
318
|
+
on ? "" : "hover:border-line-strong hover:bg-surface"
|
|
319
|
+
}`}
|
|
320
|
+
>
|
|
321
|
+
{on ? (
|
|
322
|
+
<m.span
|
|
323
|
+
layoutId="tour-ring"
|
|
324
|
+
transition={transitions.settle}
|
|
325
|
+
className="absolute inset-0 rounded-card border border-accent bg-accent/5"
|
|
326
|
+
aria-hidden
|
|
327
|
+
/>
|
|
328
|
+
) : null}
|
|
329
|
+
<div className="relative flex items-center gap-3">
|
|
330
|
+
<span
|
|
331
|
+
className={`mono flex size-8 items-center justify-center rounded-control border transition-colors ${
|
|
332
|
+
on
|
|
333
|
+
? "border-accent text-accent"
|
|
334
|
+
: "border-line text-muted group-hover:text-ink"
|
|
335
|
+
}`}
|
|
336
|
+
>
|
|
337
|
+
<Icon size={15} aria-hidden />
|
|
338
|
+
</span>
|
|
339
|
+
<div>
|
|
340
|
+
<div className="mono text-faint">{tour.number}</div>
|
|
341
|
+
<div className="font-semibold text-ink">{tour.title}</div>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
<p className="relative mt-3 text-sm text-muted">{tour.copy}</p>
|
|
345
|
+
</button>
|
|
346
|
+
);
|
|
347
|
+
})}
|
|
348
|
+
</div>
|
|
349
|
+
</LayoutGroup>
|
|
350
|
+
|
|
351
|
+
<div id="landing-product-demo" role="tabpanel" aria-live="polite">
|
|
352
|
+
<DraggableReveal>
|
|
353
|
+
<ProductFrame
|
|
354
|
+
title={`${selected.title} · portolan`}
|
|
355
|
+
eyebrow={active}
|
|
356
|
+
// The two canvases want every pixel of width they can get.
|
|
357
|
+
aside={active === "contract" || active === "problems"}
|
|
358
|
+
>
|
|
359
|
+
<AnimatePresence mode="wait" initial={false}>
|
|
360
|
+
<m.div key={active} {...page} className="relative min-h-[470px]">
|
|
361
|
+
<Suspense fallback={<DiagramSkeleton />}>
|
|
362
|
+
<Demo id={active} />
|
|
363
|
+
</Suspense>
|
|
364
|
+
</m.div>
|
|
365
|
+
</AnimatePresence>
|
|
366
|
+
</ProductFrame>
|
|
367
|
+
</DraggableReveal>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
);
|
|
371
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// The landing's link into the catalog it demonstrates.
|
|
2
|
+
//
|
|
3
|
+
// The landing shows the example estate (data.ts picks that profile for the
|
|
4
|
+
// `/landing` route), and every "open this in the catalog" it offers has to
|
|
5
|
+
// land on the same estate after a reload as it does on a click. So the profile
|
|
6
|
+
// is spelled on the URL, the way the catalog app itself keeps it there.
|
|
7
|
+
|
|
8
|
+
import { useEffect } from "react";
|
|
9
|
+
import { activeCatalogProfile } from "../data";
|
|
10
|
+
import { useSelectionStore } from "../selection/store";
|
|
11
|
+
|
|
12
|
+
export function catalogTo(pathname: string): { pathname: string; search: string } {
|
|
13
|
+
return { pathname, search: `?catalog=${activeCatalogProfile.id}` };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The canvases on the landing write to the app's one selection store, the
|
|
18
|
+
* same as they do on their own pages. A demo that is unmounted - a tab
|
|
19
|
+
* switched, the page left - must not leave its selection behind for the
|
|
20
|
+
* catalog's detail panel to open on.
|
|
21
|
+
*/
|
|
22
|
+
export function useSelectionCleared(): void {
|
|
23
|
+
const clear = useSelectionStore((s) => s.clear);
|
|
24
|
+
useEffect(() => () => clear("diagram"), [clear]);
|
|
25
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Motion the landing adds on top of lib/motion: things that arrive as the
|
|
2
|
+
// reader scrolls to them, and a hero that arrives in order. Same clock as the
|
|
3
|
+
// app - the narrative duration and the ease-out - so a card rising on the
|
|
4
|
+
// landing rises the way a row rises in the catalog.
|
|
5
|
+
|
|
6
|
+
import type { ReactNode } from "react";
|
|
7
|
+
import type { Variants } from "motion/react";
|
|
8
|
+
import { DURATION, EASE_OUT, m } from "../lib/motion";
|
|
9
|
+
|
|
10
|
+
const VIEWPORT = { once: true, margin: "-10% 0px -10% 0px" } as const;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A block that rises the fourteen pixels once, the first time it scrolls into
|
|
14
|
+
* view. `delay` staggers siblings; keep it under a third of a second, or the
|
|
15
|
+
* last card in a row is still arriving when the reader has read the first.
|
|
16
|
+
*/
|
|
17
|
+
export function Reveal({
|
|
18
|
+
children,
|
|
19
|
+
delay = 0,
|
|
20
|
+
className,
|
|
21
|
+
}: {
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
delay?: number;
|
|
24
|
+
className?: string;
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<m.div
|
|
28
|
+
className={className}
|
|
29
|
+
initial={{ opacity: 0, y: 14 }}
|
|
30
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
31
|
+
viewport={VIEWPORT}
|
|
32
|
+
transition={{ duration: DURATION.narrative, ease: EASE_OUT, delay }}
|
|
33
|
+
>
|
|
34
|
+
{children}
|
|
35
|
+
</m.div>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The hero's column: eyebrow, headline, copy, buttons, one after another. */
|
|
40
|
+
export const heroColumn: Variants = {
|
|
41
|
+
hidden: {},
|
|
42
|
+
shown: { transition: { staggerChildren: 0.08, delayChildren: 0.05 } },
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const heroLine: Variants = {
|
|
46
|
+
hidden: { opacity: 0, y: 14 },
|
|
47
|
+
shown: {
|
|
48
|
+
opacity: 1,
|
|
49
|
+
y: 0,
|
|
50
|
+
transition: { duration: DURATION.narrative, ease: EASE_OUT },
|
|
51
|
+
},
|
|
52
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Every reading the catalog makes of itself, in one list.
|
|
2
|
+
//
|
|
3
|
+
// Four readers, each answering a different question: edges that resolve to
|
|
4
|
+
// nothing, schemas that name a method the module lacks, tables and columns
|
|
5
|
+
// that disagree with the model, and channels that documents and events do not
|
|
6
|
+
// agree on. The problems page, the overview and the landing all want the
|
|
7
|
+
// union, errors first - a boundary leak is not the same kind of news as a
|
|
8
|
+
// column whose type has drifted, and mixing them buries the first.
|
|
9
|
+
|
|
10
|
+
import type { Catalog, CatalogIndex } from "../catalog";
|
|
11
|
+
import { dataProblems } from "./data-problems";
|
|
12
|
+
import { problems } from "./derive";
|
|
13
|
+
import type { Problem } from "./derive";
|
|
14
|
+
import { protoProblems } from "./proto-problems";
|
|
15
|
+
import { wireProblems } from "./wire-problems";
|
|
16
|
+
|
|
17
|
+
export function allProblems(catalog: Catalog, index: CatalogIndex): Problem[] {
|
|
18
|
+
const found = [
|
|
19
|
+
...problems(catalog),
|
|
20
|
+
...protoProblems(catalog, index),
|
|
21
|
+
...dataProblems(catalog, index),
|
|
22
|
+
...wireProblems(catalog, index),
|
|
23
|
+
];
|
|
24
|
+
return [
|
|
25
|
+
...found.filter((p) => p.severity === "error"),
|
|
26
|
+
...found.filter((p) => p.severity === "warning"),
|
|
27
|
+
];
|
|
28
|
+
}
|
package/src/lib/local-api.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SetupInfo, SetupPhase, SetupRunStepStatus } from "./setup-info";
|
|
2
2
|
|
|
3
|
-
const ROOT =
|
|
3
|
+
const ROOT = `${import.meta.env.BASE_URL}__portolan`;
|
|
4
4
|
const LOCAL_HEADER = { "Content-Type": "application/json", "X-Portolan-Local": "1" };
|
|
5
5
|
|
|
6
6
|
export interface Detection {
|
|
@@ -64,6 +64,27 @@ export interface GeneratedFileDiff {
|
|
|
64
64
|
diff: string;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
export type DeliveryProvider = "github" | "gitlab";
|
|
68
|
+
|
|
69
|
+
export interface DeliveryPresetFile {
|
|
70
|
+
path: string;
|
|
71
|
+
status: "added" | "changed" | "unchanged" | "conflict";
|
|
72
|
+
diff: string;
|
|
73
|
+
message?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface DeliveryPreset {
|
|
77
|
+
provider: DeliveryProvider;
|
|
78
|
+
detectedProvider: DeliveryProvider | null;
|
|
79
|
+
remote: string | null;
|
|
80
|
+
repository: string;
|
|
81
|
+
defaultBranch: string;
|
|
82
|
+
status: "available" | "installed" | "update" | "conflict";
|
|
83
|
+
revision: string;
|
|
84
|
+
features: string[];
|
|
85
|
+
files: DeliveryPresetFile[];
|
|
86
|
+
}
|
|
87
|
+
|
|
67
88
|
export interface ProjectTrialFact {
|
|
68
89
|
key: string;
|
|
69
90
|
label: string;
|
|
@@ -142,6 +163,19 @@ export async function localStatus(): Promise<{ local: true; workspace: string; s
|
|
|
142
163
|
return json("/status");
|
|
143
164
|
}
|
|
144
165
|
|
|
166
|
+
export async function previewDeliveryPreset(provider?: DeliveryProvider): Promise<DeliveryPreset> {
|
|
167
|
+
const query = provider ? `?provider=${encodeURIComponent(provider)}` : "";
|
|
168
|
+
return json(`/delivery-presets${query}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function installDeliveryPreset(provider: DeliveryProvider, revision: string): Promise<DeliveryPreset & { written: string[] }> {
|
|
172
|
+
return json("/delivery-presets/install", {
|
|
173
|
+
method: "POST",
|
|
174
|
+
headers: LOCAL_HEADER,
|
|
175
|
+
body: JSON.stringify({ provider, revision }),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
145
179
|
export async function discover(path: string): Promise<Discovery> {
|
|
146
180
|
return json("/discover", { method: "POST", headers: LOCAL_HEADER, body: JSON.stringify({ path }) });
|
|
147
181
|
}
|
|
@@ -16,6 +16,7 @@ describe("publicSetupFrom", () => {
|
|
|
16
16
|
},
|
|
17
17
|
{ name: "docs", wasm: { url: "https://example.com/docs.wasm" } },
|
|
18
18
|
{ name: "unused", process: { command: "unused" } },
|
|
19
|
+
{ name: "vendor", host: "fetch-git" },
|
|
19
20
|
],
|
|
20
21
|
extract: [
|
|
21
22
|
{
|
|
@@ -58,6 +59,13 @@ describe("publicSetupFrom", () => {
|
|
|
58
59
|
stepCount: 0,
|
|
59
60
|
projectIds: [],
|
|
60
61
|
},
|
|
62
|
+
{
|
|
63
|
+
name: "vendor",
|
|
64
|
+
runtime: "host",
|
|
65
|
+
phases: [],
|
|
66
|
+
stepCount: 0,
|
|
67
|
+
projectIds: [],
|
|
68
|
+
},
|
|
61
69
|
]);
|
|
62
70
|
expect(JSON.stringify(setup)).not.toContain("secret");
|
|
63
71
|
expect(JSON.stringify(setup)).not.toContain("command");
|
package/src/lib/setup-info.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface SetupStep {
|
|
|
32
32
|
|
|
33
33
|
export interface SetupPlugin {
|
|
34
34
|
name: string;
|
|
35
|
-
runtime: "wasm" | "process";
|
|
35
|
+
runtime: "wasm" | "process" | "host";
|
|
36
36
|
phases: SetupPhase[];
|
|
37
37
|
stepCount: number;
|
|
38
38
|
projectIds: string[];
|
|
@@ -82,6 +82,7 @@ interface ManifestPlugin {
|
|
|
82
82
|
name?: unknown;
|
|
83
83
|
wasm?: unknown;
|
|
84
84
|
process?: unknown;
|
|
85
|
+
host?: unknown;
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
interface ManifestStep {
|
|
@@ -345,7 +346,7 @@ function pluginFrom(
|
|
|
345
346
|
// Settings prints this as a promise about filesystem, network and
|
|
346
347
|
// environment access, so a `wasm` that is not a module - null, a bare
|
|
347
348
|
// true, a half-written object - answers "process", which promises nothing.
|
|
348
|
-
runtime: typeof record(item.wasm)["url"] === "string" ? "wasm" : "process",
|
|
349
|
+
runtime: typeof record(item.wasm)["url"] === "string" ? "wasm" : typeof item.host === "string" ? "host" : "process",
|
|
349
350
|
phases: PHASES.filter((phase) => own.some((step) => step.phase === phase)),
|
|
350
351
|
stepCount: own.length,
|
|
351
352
|
projectIds: [
|
package/src/lib/source-code.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RemoteSourceLocation, SourceLocation } from "./source-link";
|
|
2
2
|
|
|
3
3
|
const MAX_SOURCE_BYTES = 1024 * 1024;
|
|
4
|
+
const LOCAL_SOURCE_URL = `${import.meta.env.BASE_URL}__portolan/source`;
|
|
4
5
|
|
|
5
6
|
export type SourceFile = {
|
|
6
7
|
content: string;
|
|
@@ -145,7 +146,7 @@ async function loadRemote(
|
|
|
145
146
|
async function loadLocal(
|
|
146
147
|
location: Extract<SourceLocation, { kind: "local" }>,
|
|
147
148
|
): Promise<SourceFile> {
|
|
148
|
-
const response = await fetch(
|
|
149
|
+
const response = await fetch(LOCAL_SOURCE_URL, {
|
|
149
150
|
method: "POST",
|
|
150
151
|
headers: { "Content-Type": "application/json", "X-Portolan-Local": "1" },
|
|
151
152
|
body: JSON.stringify({ path: location.path }),
|