@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
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { mkdtempSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdtempSync, mkdirSync, readFileSync, symlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
2
3
|
import { tmpdir } from "node:os";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { afterEach, describe, expect, it } from "vitest";
|
|
5
6
|
import { rmSync } from "node:fs";
|
|
6
7
|
|
|
7
|
-
import { classifyRepositoryFailure, diffGeneratedFiles, discoverProject, forgetRepositoryCredential, inspectionRoot, planProject, readLocalSource, resolveRepositoryCommit, storeRepositoryCredential, summarizeProjectTrial, writeProject } from "./local-api.mjs";
|
|
8
|
+
import { classifyRepositoryFailure, diffGeneratedFiles, discoverProject, forgetRepositoryCredential, inspectionRoot, localApiPath, planProject, readLocalSource, resolveRepositoryCommit, storeRepositoryCredential, summarizeProjectTrial, writeProject } from "./local-api.mjs";
|
|
9
|
+
import { installDeliveryPreset, planDeliveryPreset, providerFromRemote, publicDeliveryPreset } from "./delivery-presets.mjs";
|
|
8
10
|
|
|
9
11
|
const roots = [];
|
|
10
12
|
afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); });
|
|
@@ -28,7 +30,87 @@ function workspace() {
|
|
|
28
30
|
return root;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
function gitWorkspace(provider = "github") {
|
|
34
|
+
const root = workspace();
|
|
35
|
+
execFileSync("git", ["init", "-q", "-b", "main"], { cwd: root });
|
|
36
|
+
const remote = provider === "github" ? "git@github.com:acme/shop.git" : "https://gitlab.com/acme/shop.git";
|
|
37
|
+
execFileSync("git", ["remote", "add", "origin", remote], { cwd: root });
|
|
38
|
+
return root;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("local API base path", () => {
|
|
42
|
+
it("matches control-plane routes below the configured site base", () => {
|
|
43
|
+
expect(localApiPath("/portolan/__portolan/status", "/portolan/")).toBe("/__portolan/status");
|
|
44
|
+
expect(localApiPath("/portolan/__portolan/status", "/portolan")).toBe("/__portolan/status");
|
|
45
|
+
expect(localApiPath("/__portolan/status", "/portolan/")).toBe("/__portolan/status");
|
|
46
|
+
expect(localApiPath("/another/status", "/portolan/")).toBe("/another/status");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
31
50
|
describe("local project setup", () => {
|
|
51
|
+
it("detects GitHub and GitLab remotes without treating arbitrary hosts as a forge", () => {
|
|
52
|
+
expect(providerFromRemote("git@github.com:acme/shop.git")).toBe("github");
|
|
53
|
+
expect(providerFromRemote("https://gitlab.example.com/acme/shop.git")).toBe("gitlab");
|
|
54
|
+
expect(providerFromRemote("ssh://git@example.com/acme/shop.git")).toBeNull();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("previews and installs GitHub delivery workflows idempotently", () => {
|
|
58
|
+
const root = gitWorkspace("github");
|
|
59
|
+
const preview = planDeliveryPreset(root);
|
|
60
|
+
expect(preview).toMatchObject({ provider: "github", detectedProvider: "github", repository: "shop", defaultBranch: "main", status: "available" });
|
|
61
|
+
expect(preview.files.map((file) => [file.path, file.status])).toEqual([
|
|
62
|
+
[".github/workflows/portolan-check.yml", "added"],
|
|
63
|
+
[".github/workflows/portolan-pages.yml", "added"],
|
|
64
|
+
]);
|
|
65
|
+
expect(publicDeliveryPreset(preview).files[0]).not.toHaveProperty("next");
|
|
66
|
+
const installed = installDeliveryPreset(root, { provider: "github", revision: preview.revision });
|
|
67
|
+
expect(installed.written).toHaveLength(2);
|
|
68
|
+
expect(installed.status).toBe("installed");
|
|
69
|
+
expect(readFileSync(join(root, ".github/workflows/portolan-check.yml"), "utf8")).toContain("shortlink-org/portolan@0.2.0");
|
|
70
|
+
expect(readFileSync(join(root, ".github/workflows/portolan-pages.yml"), "utf8")).toContain("base: /${{ github.event.repository.name }}/");
|
|
71
|
+
expect(installDeliveryPreset(root, { provider: "github", revision: installed.revision }).written).toEqual([]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("refuses to overwrite an unmanaged GitHub workflow or apply a stale preview", () => {
|
|
75
|
+
const root = gitWorkspace("github");
|
|
76
|
+
mkdirSync(join(root, ".github/workflows"), { recursive: true });
|
|
77
|
+
writeFileSync(join(root, ".github/workflows/portolan-check.yml"), "name: Mine\n");
|
|
78
|
+
const conflict = planDeliveryPreset(root, "github");
|
|
79
|
+
expect(conflict.status).toBe("conflict");
|
|
80
|
+
expect(() => installDeliveryPreset(root, { provider: "github", revision: conflict.revision })).toThrow(/was not created by Portolan/);
|
|
81
|
+
const clean = gitWorkspace("github");
|
|
82
|
+
const preview = planDeliveryPreset(clean, "github");
|
|
83
|
+
mkdirSync(join(clean, ".github/workflows"), { recursive: true });
|
|
84
|
+
writeFileSync(join(clean, ".github/workflows/portolan-check.yml"), "name: Arrived later\n");
|
|
85
|
+
expect(() => installDeliveryPreset(clean, { provider: "github", revision: preview.revision })).toThrow(/Preview it again/);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("refuses a delivery target below a symlinked directory", () => {
|
|
89
|
+
const root = gitWorkspace("github");
|
|
90
|
+
const outside = mkdtempSync(join(tmpdir(), "portolan-preset-outside-"));
|
|
91
|
+
roots.push(outside);
|
|
92
|
+
symlinkSync(outside, join(root, ".github"), "dir");
|
|
93
|
+
const preview = planDeliveryPreset(root, "github");
|
|
94
|
+
expect(preview.status).toBe("conflict");
|
|
95
|
+
expect(() => installDeliveryPreset(root, { provider: "github", revision: preview.revision })).toThrow(/was not created by Portolan/);
|
|
96
|
+
expect(existsSync(join(outside, "workflows/portolan-check.yml"))).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("adds and updates one managed GitLab region while preserving the pipeline", () => {
|
|
100
|
+
const root = gitWorkspace("gitlab");
|
|
101
|
+
writeFileSync(join(root, ".gitlab-ci.yml"), "lint:\n script: echo lint\n");
|
|
102
|
+
const preview = planDeliveryPreset(root);
|
|
103
|
+
expect(preview).toMatchObject({ provider: "gitlab", detectedProvider: "gitlab", status: "available" });
|
|
104
|
+
expect(preview.files[0].diff).toContain("+\"portolan:check\":");
|
|
105
|
+
const installed = installDeliveryPreset(root, { provider: "gitlab", revision: preview.revision });
|
|
106
|
+
expect(installed.status).toBe("installed");
|
|
107
|
+
const pipeline = readFileSync(join(root, ".gitlab-ci.yml"), "utf8");
|
|
108
|
+
expect(pipeline).toContain("lint:\n script: echo lint");
|
|
109
|
+
expect(pipeline).toContain("# >>> Portolan delivery preset >>>");
|
|
110
|
+
expect(pipeline).toContain("pages:\n publish: dist");
|
|
111
|
+
expect(existsSync(join(root, ".github"))).toBe(false);
|
|
112
|
+
});
|
|
113
|
+
|
|
32
114
|
it("classifies repository authentication, authorization, and timeout failures", () => {
|
|
33
115
|
expect(classifyRepositoryFailure(new Error("fatal: Authentication failed"), "GitHub")).toMatchObject({
|
|
34
116
|
code: "repository_auth_required", status: 401, retryable: true, provider: "GitHub",
|
package/scripts/manifest.mjs
CHANGED
|
@@ -13,7 +13,8 @@ import { normalize } from "node:path";
|
|
|
13
13
|
|
|
14
14
|
import Ajv from "ajv/dist/2020.js";
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// Read when asked, not when loaded: the CLI sets PORTOLAN_SCHEMA after its imports.
|
|
17
|
+
const schemaFile = () => process.env.PORTOLAN_SCHEMA || "schema/portolan.schema.json";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Reads the manifest and validates it.
|
|
@@ -28,7 +29,7 @@ export function loadManifest(path = "portolan.json") {
|
|
|
28
29
|
|
|
29
30
|
let schema;
|
|
30
31
|
try {
|
|
31
|
-
schema = JSON.parse(readFileSync(
|
|
32
|
+
schema = JSON.parse(readFileSync(schemaFile(), "utf8"));
|
|
32
33
|
} catch {
|
|
33
34
|
return { manifest, problems: [] };
|
|
34
35
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// This catches accidental cwd coupling before the same package reaches npm.
|
|
3
3
|
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
|
-
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
@@ -14,6 +14,10 @@ const fixture = mkdtempSync(resolve(tmpdir(), "portolan-package-smoke-"));
|
|
|
14
14
|
try {
|
|
15
15
|
writeFileSync(resolve(fixture, "package.json"), '{"name":"package-smoke","version":"1.0.0"}\n');
|
|
16
16
|
writeFileSync(resolve(fixture, "README.md"), "# Package smoke\n");
|
|
17
|
+
// A Go domain layout: init should notice it and wire the extractor without being asked.
|
|
18
|
+
writeFileSync(resolve(fixture, "go.mod"), "module example.com/smoke\n\ngo 1.24\n");
|
|
19
|
+
mkdirSync(resolve(fixture, "internal/domain/order"), { recursive: true });
|
|
20
|
+
writeFileSync(resolve(fixture, "internal/domain/order/order.go"), "package order\n\ntype Order struct{ ID string }\n");
|
|
17
21
|
run("git", ["init", "--quiet"]);
|
|
18
22
|
run("git", ["config", "user.email", "portolan@example.invalid"]);
|
|
19
23
|
run("git", ["config", "user.name", "Portolan smoke test"]);
|
|
@@ -24,7 +28,7 @@ try {
|
|
|
24
28
|
run(process.execPath, [cli, "check", "--cwd", fixture]);
|
|
25
29
|
run(process.execPath, [cli, "build", "--cwd", fixture, "--output", "dist", "--base", "/architecture/"]);
|
|
26
30
|
|
|
27
|
-
for (const path of ["portolan/project.json", "docs/README.md", "dist/index.html", "dist/404.html"]) {
|
|
31
|
+
for (const path of ["portolan/project.json", "portolan/domain.json", "docs/README.md", "dist/index.html", "dist/404.html"]) {
|
|
28
32
|
if (!existsSync(resolve(fixture, path))) throw new Error(`smoke test did not write ${path}`);
|
|
29
33
|
}
|
|
30
34
|
console.log("package smoke: init, generate, check, and build passed outside the repository");
|
package/scripts/plugin-host.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
// Runs a portolan
|
|
1
|
+
// Runs a portolan plugin and returns what it wants written.
|
|
2
2
|
//
|
|
3
|
-
// A plugin never
|
|
4
|
-
// with named files on stdout; writing them - and refusing a
|
|
5
|
-
// out of the output directory - is this file's job. That split
|
|
6
|
-
//
|
|
7
|
-
// all,
|
|
8
|
-
//
|
|
3
|
+
// A plugin never writes the tree (portolan.0001). It is handed a request on
|
|
4
|
+
// stdin and answers with named files on stdout; writing them - and refusing a
|
|
5
|
+
// name that climbs out of the output directory - is this file's job. That split
|
|
6
|
+
// is what lets a generator run as a wasm module with no directory preopened at
|
|
7
|
+
// all, an extractor as one with only the workspace preopened (portolan.0006),
|
|
8
|
+
// and it is why `--check` can compare a render against disk without the plugin
|
|
9
|
+
// knowing there is a disk.
|
|
9
10
|
|
|
10
11
|
import { spawn } from "node:child_process";
|
|
11
12
|
import { createHash } from "node:crypto";
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
} from "node:fs";
|
|
19
20
|
import { mkdtemp } from "node:fs/promises";
|
|
20
21
|
import { tmpdir } from "node:os";
|
|
21
|
-
import { isAbsolute, join } from "node:path";
|
|
22
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
22
23
|
import { Worker } from "node:worker_threads";
|
|
23
24
|
|
|
24
25
|
const PORTOLAN_VERSION = "0.1.0";
|
|
@@ -35,9 +36,14 @@ const HOST_LIMITS = Object.freeze({
|
|
|
35
36
|
*
|
|
36
37
|
* @param {{name: string, wasm?: {url: string, sha256?: string}, process?: {command: string, args?: string[]}}} plugin
|
|
37
38
|
* @param {unknown} request
|
|
39
|
+
* @param {{workspace?: string}} [access] `workspace` is the directory a wasm
|
|
40
|
+
* module may read, preopened as `/` (portolan.0006). Left out, the module
|
|
41
|
+
* gets no filesystem at all, which is what a generator and a describe
|
|
42
|
+
* request should get. A process plugin is unaffected: it already has the
|
|
43
|
+
* whole machine.
|
|
38
44
|
* @returns {Promise<{files: {name: string, contents: string}[], describe?: object}>}
|
|
39
45
|
*/
|
|
40
|
-
export async function runPlugin(plugin, request, requestedLimits = {}) {
|
|
46
|
+
export async function runPlugin(plugin, request, requestedLimits = {}, access = {}) {
|
|
41
47
|
const limits = lowerLimits(requestedLimits);
|
|
42
48
|
let payload;
|
|
43
49
|
try {
|
|
@@ -52,8 +58,13 @@ export async function runPlugin(plugin, request, requestedLimits = {}) {
|
|
|
52
58
|
throw new Error(`plugin ${plugin.name}: request exceeds ${limits.requestBytes} bytes`);
|
|
53
59
|
}
|
|
54
60
|
|
|
61
|
+
if (plugin.host) {
|
|
62
|
+
const { response, warnings } = await runHost(plugin, request);
|
|
63
|
+
return { ...validateResponse(plugin.name, response), warnings };
|
|
64
|
+
}
|
|
65
|
+
|
|
55
66
|
const result = plugin.wasm
|
|
56
|
-
? await runWasm(plugin, payload, limits)
|
|
67
|
+
? await runWasm(plugin, payload, limits, access)
|
|
57
68
|
: await runProcess(plugin, payload, limits);
|
|
58
69
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
59
70
|
|
|
@@ -69,6 +80,31 @@ export async function runPlugin(plugin, request, requestedLimits = {}) {
|
|
|
69
80
|
return { ...validateResponse(plugin.name, response), warnings: warningsIn(result.stderr) };
|
|
70
81
|
}
|
|
71
82
|
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// host
|
|
85
|
+
//
|
|
86
|
+
// A plugin that needs what only the host has - a socket, a git binary - and
|
|
87
|
+
// so runs inside it (portolan.0008). It speaks the same protocol as any other:
|
|
88
|
+
// a request in, named files out, warnings beside them; the host writes and
|
|
89
|
+
// validates exactly as it would for a module. Only the modules shipped here
|
|
90
|
+
// can be named, so a manifest cannot point the host at arbitrary code.
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
const HOST_PLUGINS = new Set(["fetch-git", "fetch-bsr", "fetch-csr"]);
|
|
94
|
+
|
|
95
|
+
async function runHost(plugin, request) {
|
|
96
|
+
const name = String(plugin.host);
|
|
97
|
+
if (!HOST_PLUGINS.has(name)) throw new Error(`plugin ${plugin.name}: the host has no plugin named ${JSON.stringify(name)}`);
|
|
98
|
+
const module = await import(`./host-plugins/${name}.mjs`);
|
|
99
|
+
if (request?.kind === "describe") return { response: { files: [], describe: module.describe() }, warnings: [] };
|
|
100
|
+
const { files, warnings = [] } = await module.run(request);
|
|
101
|
+
// Said the way a module says it on stderr, and kept the way warningsIn
|
|
102
|
+
// keeps it, so the build report reads the same whoever ran the step.
|
|
103
|
+
const lines = warnings.map((warning) => `${warning.ref ? `${warning.ref}: ` : ""}${warning.message}`);
|
|
104
|
+
for (const line of lines) process.stderr.write(`warning: ${line}\n`);
|
|
105
|
+
return { response: { files }, warnings: lines };
|
|
106
|
+
}
|
|
107
|
+
|
|
72
108
|
/**
|
|
73
109
|
* The warnings a plugin wrote to stderr, one per `warning: ` line, without the
|
|
74
110
|
* prefix.
|
|
@@ -164,6 +200,9 @@ function validateDescriptor(pluginName, descriptor) {
|
|
|
164
200
|
if (!descriptor.options || typeof descriptor.options !== "object" || Array.isArray(descriptor.options)) {
|
|
165
201
|
throw new Error(`plugin ${pluginName}: describe.options is invalid`);
|
|
166
202
|
}
|
|
203
|
+
if (descriptor.needs !== undefined && (!Array.isArray(descriptor.needs) || descriptor.needs.some((need) => need !== "history"))) {
|
|
204
|
+
throw new Error(`plugin ${pluginName}: describe.needs may only name "history"`);
|
|
205
|
+
}
|
|
167
206
|
}
|
|
168
207
|
|
|
169
208
|
/**
|
|
@@ -191,12 +230,29 @@ export async function describePlugin(plugin) {
|
|
|
191
230
|
//
|
|
192
231
|
// Node's WASI takes file descriptors rather than pipes, so the request and the
|
|
193
232
|
// response go through a scratch directory. That is the only place the sandbox
|
|
194
|
-
// leaks, and it leaks into a temporary directory the plugin is not told about
|
|
195
|
-
//
|
|
233
|
+
// leaks, and it leaks into a temporary directory the plugin is not told about.
|
|
234
|
+
// A generator gets no preopen at all. An extract or verify step gets the
|
|
235
|
+
// workspace as `/` (portolan.0006) - read-write, because that is all WASI
|
|
236
|
+
// offers, and nothing beyond it: no network, no environment, no way to spawn.
|
|
196
237
|
// ---------------------------------------------------------------------------
|
|
197
238
|
|
|
198
|
-
|
|
239
|
+
/** Compiled modules by digest: one module answers for every built-in plugin. */
|
|
240
|
+
const compiled = new Map();
|
|
241
|
+
|
|
242
|
+
async function compileWasm(plugin, limits) {
|
|
199
243
|
const bytes = await loadWasm(plugin, limits);
|
|
244
|
+
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
245
|
+
let module = compiled.get(digest);
|
|
246
|
+
if (!module) {
|
|
247
|
+
module = await WebAssembly.compile(bytes);
|
|
248
|
+
compiled.set(digest, module);
|
|
249
|
+
}
|
|
250
|
+
return module;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function runWasm(plugin, payload, limits, access) {
|
|
254
|
+
const module = await compileWasm(plugin, limits);
|
|
255
|
+
const workspace = access.workspace ? resolve(access.workspace) : "";
|
|
200
256
|
|
|
201
257
|
const dir = await mkdtemp(join(tmpdir(), "portolan-plugin-"));
|
|
202
258
|
const inPath = join(dir, "request.json");
|
|
@@ -209,7 +265,10 @@ async function runWasm(plugin, payload, limits) {
|
|
|
209
265
|
|
|
210
266
|
try {
|
|
211
267
|
const worker = new Worker(new URL("./plugin-wasm-worker.mjs", import.meta.url), {
|
|
212
|
-
workerData: { name: plugin.name,
|
|
268
|
+
workerData: { name: plugin.name, module, workspace, inPath, outPath, errPath },
|
|
269
|
+
// node:wasi announces itself as experimental on every import; once per
|
|
270
|
+
// step, that is sixty lines of noise in a generate.
|
|
271
|
+
execArgv: [...process.execArgv, "--disable-warning=ExperimentalWarning"],
|
|
213
272
|
});
|
|
214
273
|
const code = await new Promise((resolve, reject) => {
|
|
215
274
|
let settled = false;
|
|
@@ -350,10 +409,10 @@ function verifyDigest(name, bytes, expected) {
|
|
|
350
409
|
// ---------------------------------------------------------------------------
|
|
351
410
|
// process
|
|
352
411
|
//
|
|
353
|
-
// The escape hatch for a
|
|
354
|
-
//
|
|
355
|
-
// protocol and none of the sandbox, which is the trade being
|
|
356
|
-
// reason it is not the default.
|
|
412
|
+
// The escape hatch for a plugin that needs a toolchain or a socket - the Rust
|
|
413
|
+
// extractor runs under Cargo, `adr` asks git for history, `fetch-git` clones.
|
|
414
|
+
// It gets the same protocol and none of the sandbox, which is the trade being
|
|
415
|
+
// made and the reason it is not the default.
|
|
357
416
|
// ---------------------------------------------------------------------------
|
|
358
417
|
|
|
359
418
|
function runProcess(plugin, payload, limits) {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
6
7
|
import { runPlugin, validateResponse, warningsIn } from "./plugin-host.mjs";
|
|
7
8
|
|
|
@@ -100,6 +101,54 @@ describe("wasm plugins", () => {
|
|
|
100
101
|
});
|
|
101
102
|
});
|
|
102
103
|
|
|
104
|
+
// The built-in module, when `npm run plugins:build` has produced it. These
|
|
105
|
+
// tests read a tree through it, which is what portolan.0006 allows an extract
|
|
106
|
+
// step and denies everything else.
|
|
107
|
+
const builtinModule = fileURLToPath(new URL("../plugins/portolan-go.wasm", import.meta.url));
|
|
108
|
+
const describeBuiltin = existsSync(builtinModule) ? describe : describe.skip;
|
|
109
|
+
|
|
110
|
+
describeBuiltin("wasm preopen", () => {
|
|
111
|
+
const plugin = { name: "project", wasm: { url: `file://${builtinModule}` } };
|
|
112
|
+
const request = {
|
|
113
|
+
portolanVersion: "0.1.0",
|
|
114
|
+
input: { root: ".", output: "portolan", commit: "abc1234", generatedAt: "2026-09-08T00:00:00Z" },
|
|
115
|
+
options: { group: "fixture", component: "fixture", componentName: "Fixture", out: "project.json" },
|
|
116
|
+
};
|
|
117
|
+
const workspace = () => {
|
|
118
|
+
const dir = mkdtempSync(join(tmpdir(), "portolan-preopen-"));
|
|
119
|
+
writeFileSync(join(dir, "README.md"), "# Fixture\n");
|
|
120
|
+
writeFileSync(join(dir, "go.mod"), "module example.com/fixture\n\ngo 1.24\n");
|
|
121
|
+
return dir;
|
|
122
|
+
};
|
|
123
|
+
const serviceIn = (result) => JSON.parse(result.files[0].contents).contexts[0].services[0];
|
|
124
|
+
|
|
125
|
+
it("reads the workspace an extract step is given", async () => {
|
|
126
|
+
const dir = workspace();
|
|
127
|
+
try {
|
|
128
|
+
const service = serviceIn(await runPlugin(plugin, request, {}, { workspace: dir }));
|
|
129
|
+
expect(service.readme).toBe("# Fixture");
|
|
130
|
+
expect(service.technologies).toContain("Go");
|
|
131
|
+
expect(service.repo).toBe("example.com/fixture");
|
|
132
|
+
} finally {
|
|
133
|
+
rmSync(dir, { recursive: true, force: true });
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("sees nothing when no workspace is given, as a generator or describe never should", async () => {
|
|
138
|
+
const dir = workspace();
|
|
139
|
+
const cwd = process.cwd();
|
|
140
|
+
process.chdir(dir);
|
|
141
|
+
try {
|
|
142
|
+
const service = serviceIn(await runPlugin(plugin, request));
|
|
143
|
+
expect(service.readme).toBe("");
|
|
144
|
+
expect(service.technologies ?? []).not.toContain("Go");
|
|
145
|
+
} finally {
|
|
146
|
+
process.chdir(cwd);
|
|
147
|
+
rmSync(dir, { recursive: true, force: true });
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
103
152
|
describe("warningsIn", () => {
|
|
104
153
|
it("reads only the warning lines, and nothing from an empty stderr", () => {
|
|
105
154
|
expect(warningsIn("")).toEqual([]);
|
|
@@ -11,13 +11,16 @@ async function run() {
|
|
|
11
11
|
version: "preview1",
|
|
12
12
|
args: [workerData.name],
|
|
13
13
|
env: {},
|
|
14
|
-
|
|
14
|
+
// Empty for a generator or a describe request. An extract or verify
|
|
15
|
+
// step gets the workspace as `/`, and nothing else (portolan.0006).
|
|
16
|
+
preopens: workerData.workspace ? { "/": workerData.workspace } : {},
|
|
15
17
|
stdin,
|
|
16
18
|
stdout,
|
|
17
19
|
stderr,
|
|
18
20
|
});
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
// The module was compiled once by the host and shared; a 20 MB module
|
|
22
|
+
// compiled per step would cost more than the step.
|
|
23
|
+
const instance = await WebAssembly.instantiate(workerData.module, wasi.getImportObject());
|
|
21
24
|
return wasi.start(instance);
|
|
22
25
|
} finally {
|
|
23
26
|
closeSync(stdin);
|
package/scripts/run-builtin.mjs
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
// Process adapter for plugins bundled in the npm package. It keeps the
|
|
4
4
|
// plugin's cwd in the user's workspace while resolving source and toolchain
|
|
5
|
-
// files from the installation. Go plugins
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// files from the installation. The Go plugins no longer come through here:
|
|
6
|
+
// they are one wasm module (portolan.0006), and the fetchers run inside the
|
|
7
|
+
// host (portolan.0008).
|
|
8
8
|
|
|
9
|
-
import { spawn
|
|
10
|
-
import { existsSync,
|
|
9
|
+
import { spawn } from "node:child_process";
|
|
10
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
11
11
|
import { dirname, resolve } from "node:path";
|
|
12
12
|
import { fileURLToPath } from "node:url";
|
|
13
13
|
|
|
@@ -25,27 +25,7 @@ if (!plugin?.process) {
|
|
|
25
25
|
const command = plugin.process.command;
|
|
26
26
|
const args = [...(plugin.process.args ?? [])];
|
|
27
27
|
|
|
28
|
-
if (command === "
|
|
29
|
-
const binDir = resolve(workspace, ".portolan", "bin");
|
|
30
|
-
const suffix = process.platform === "win32" ? ".exe" : "";
|
|
31
|
-
const binary = resolve(binDir, `${name}${suffix}`);
|
|
32
|
-
mkdirSync(binDir, { recursive: true });
|
|
33
|
-
|
|
34
|
-
// Rebuilding is deliberately left to Go's build cache. Package upgrades can
|
|
35
|
-
// change shared extractor code without changing this plugin directory, so a
|
|
36
|
-
// timestamp shortcut here would make an old binary look current.
|
|
37
|
-
const built = spawnSync(
|
|
38
|
-
"go",
|
|
39
|
-
["build", "-o", binary, args[1]],
|
|
40
|
-
{ cwd: installRoot, stdio: ["ignore", "ignore", "inherit"] },
|
|
41
|
-
);
|
|
42
|
-
if (built.error) {
|
|
43
|
-
console.error(`portolan: could not start Go to build ${name}: ${built.error.message}`);
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
if (built.status !== 0 || !existsSync(binary)) process.exit(built.status ?? 1);
|
|
47
|
-
run(binary, []);
|
|
48
|
-
} else if (command === "cargo" && existsSync(resolve(installRoot, "plugins/extract-rust/target/release/portolan-extract-rust"))) {
|
|
28
|
+
if (command === "cargo" && existsSync(resolve(installRoot, "plugins/extract-rust/target/release/portolan-extract-rust"))) {
|
|
49
29
|
run(resolve(installRoot, "plugins/extract-rust/target/release/portolan-extract-rust"), []);
|
|
50
30
|
} else {
|
|
51
31
|
if (command === "cargo") {
|
package/scripts/schema.mjs
CHANGED
|
@@ -226,12 +226,17 @@ function compose() {
|
|
|
226
226
|
type: "object",
|
|
227
227
|
additionalProperties: false,
|
|
228
228
|
required: ["name"],
|
|
229
|
-
oneOf: [{ required: ["wasm"] }, { required: ["process"] }],
|
|
229
|
+
oneOf: [{ required: ["wasm"] }, { required: ["process"] }, { required: ["host"] }],
|
|
230
230
|
properties: {
|
|
231
231
|
name: {
|
|
232
232
|
type: "string",
|
|
233
233
|
description: "What the steps below call this plugin. It need not be the plugin's own name.",
|
|
234
234
|
},
|
|
235
|
+
host: {
|
|
236
|
+
enum: ["fetch-git", "fetch-bsr", "fetch-csr"],
|
|
237
|
+
description:
|
|
238
|
+
"A plugin the host runs inside its own process, because it needs a socket or a git binary (portolan.0008). Only the ones shipped with Portolan can be named.",
|
|
239
|
+
},
|
|
235
240
|
wasm: {
|
|
236
241
|
type: "object",
|
|
237
242
|
additionalProperties: false,
|