@fungi.computer/oyster 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +1134 -2
- package/dist/agents.d.ts +114 -0
- package/dist/agents.js +322 -0
- package/dist/app-builds.d.ts +210 -0
- package/dist/app-builds.js +499 -0
- package/dist/app-sandbox.d.ts +2 -0
- package/dist/app-sandbox.js +2 -0
- package/dist/app-sources.d.ts +31 -0
- package/dist/app-sources.js +370 -0
- package/dist/app-storage.d.ts +141 -0
- package/dist/app-storage.js +281 -0
- package/dist/apps/gate.d.ts +105 -0
- package/dist/apps/gate.js +273 -0
- package/dist/apps/index.d.ts +5 -0
- package/dist/apps/index.js +5 -0
- package/dist/apps/installation.d.ts +248 -0
- package/dist/apps/installation.js +307 -0
- package/dist/apps/operations.d.ts +8 -0
- package/dist/apps/operations.js +98 -0
- package/dist/apps/tools.d.ts +33 -0
- package/dist/apps/tools.js +17 -0
- package/dist/auth.d.ts +11 -0
- package/dist/auth.js +93 -0
- package/dist/backend-request.d.ts +3 -0
- package/dist/backend-request.js +53 -0
- package/dist/build/artifact-writer.d.ts +40 -0
- package/dist/build/artifact-writer.js +181 -0
- package/dist/build/build-jobs.d.ts +128 -0
- package/dist/build/build-jobs.js +203 -0
- package/dist/build/build-policy.d.ts +10 -0
- package/dist/build/build-policy.js +10 -0
- package/dist/build/build-step.d.ts +37 -0
- package/dist/build/build-step.js +28 -0
- package/dist/build/bundled-html.d.ts +7 -0
- package/dist/build/bundled-html.js +64 -0
- package/dist/build/candidate-builder.d.ts +68 -0
- package/dist/build/candidate-builder.js +91 -0
- package/dist/build/candidate-cli.d.ts +1 -0
- package/dist/build/candidate-cli.js +119 -0
- package/dist/build/candidate-cli.mjs +28170 -0
- package/dist/build/content-objects.d.ts +37 -0
- package/dist/build/content-objects.js +53 -0
- package/dist/build/definition-emission.d.ts +34 -0
- package/dist/build/definition-emission.js +201 -0
- package/dist/build/effect.d.ts +249 -0
- package/dist/build/effect.js +436 -0
- package/dist/build/git-environment.d.ts +9 -0
- package/dist/build/git-environment.js +60 -0
- package/dist/build/html-recipe.d.ts +20 -0
- package/dist/build/html-recipe.js +51 -0
- package/dist/build/local-build.d.ts +67 -0
- package/dist/build/local-build.js +1128 -0
- package/dist/build/snapshot-package-store.d.ts +12 -0
- package/dist/build/snapshot-package-store.js +36 -0
- package/dist/build/snapshot-program-scripts.d.ts +6 -0
- package/dist/build/snapshot-program-scripts.js +2 -0
- package/dist/build/snapshot-programs/snapshot-path.d.ts +2 -0
- package/dist/build/snapshot-programs/snapshot-path.js +6 -0
- package/dist/build/submission-contract.d.ts +828 -0
- package/dist/build/submission-contract.js +180 -0
- package/dist/build/workspace-build.d.ts +55 -0
- package/dist/build/workspace-build.js +200 -0
- package/dist/build-contract.d.ts +7 -0
- package/dist/build-contract.js +7 -0
- package/dist/build-egress.d.ts +62 -0
- package/dist/build-egress.js +148 -0
- package/dist/build-jobs.d.ts +2 -0
- package/dist/build-jobs.js +2 -0
- package/dist/build.d.ts +12 -0
- package/dist/build.js +14 -0
- package/dist/capability-descriptor.d.ts +46 -0
- package/dist/capability-descriptor.js +51 -0
- package/dist/catalog-frame.d.ts +39 -0
- package/dist/catalog-frame.js +72 -0
- package/dist/cause-class.d.ts +19 -0
- package/dist/cause-class.js +54 -0
- package/dist/cloudflare.d.ts +123 -0
- package/dist/cloudflare.js +391 -0
- package/dist/computer-terminal.d.ts +38 -0
- package/dist/computer-terminal.js +410 -0
- package/dist/computer.d.ts +60 -0
- package/dist/computer.js +33 -0
- package/dist/event-recipients.d.ts +57 -0
- package/dist/event-recipients.js +144 -0
- package/dist/facet-invocation.d.ts +4 -0
- package/dist/facet-invocation.js +45 -0
- package/dist/files.d.ts +42 -0
- package/dist/files.js +123 -0
- package/dist/frame-binding-wire-script.d.ts +2 -0
- package/dist/frame-binding-wire-script.js +1 -0
- package/dist/frame-binding-wire.d.ts +28 -0
- package/dist/frame-binding-wire.js +54 -0
- package/dist/git-admission.d.ts +52 -0
- package/dist/git-admission.js +188 -0
- package/dist/git-auth.d.ts +11 -0
- package/dist/git-auth.js +27 -0
- package/dist/git-door.d.ts +35 -0
- package/dist/git-door.js +108 -0
- package/dist/git-journal.d.ts +31 -0
- package/dist/git-journal.js +188 -0
- package/dist/git.d.ts +31 -0
- package/dist/git.js +64 -0
- package/dist/guest-policy.d.ts +9 -0
- package/dist/guest-policy.js +13 -0
- package/dist/host.d.ts +244 -0
- package/dist/host.js +1043 -0
- package/dist/index.d.ts +166 -0
- package/dist/index.js +320 -0
- package/dist/installation-consent.d.ts +79 -0
- package/dist/installation-consent.js +82 -0
- package/dist/internal/background-origins.d.ts +3 -0
- package/dist/internal/background-origins.js +8 -0
- package/dist/internal/delivery.d.ts +12 -0
- package/dist/internal/delivery.js +167 -0
- package/dist/internal/host-gate.d.ts +14 -0
- package/dist/internal/host-gate.js +62 -0
- package/dist/internal/registry-installations.d.ts +6 -0
- package/dist/internal/registry-installations.js +12 -0
- package/dist/invocation.d.ts +251 -0
- package/dist/invocation.js +377 -0
- package/dist/keys.d.ts +48 -0
- package/dist/keys.js +52 -0
- package/dist/mcp-owner.d.ts +27 -0
- package/dist/mcp-owner.js +155 -0
- package/dist/mcp.d.ts +89 -0
- package/dist/mcp.js +429 -0
- package/dist/media.d.ts +23 -0
- package/dist/media.js +12 -0
- package/dist/pack.d.ts +79 -0
- package/dist/pack.js +151 -0
- package/dist/page/capabilities.d.ts +23 -0
- package/dist/page/capabilities.js +256 -0
- package/dist/page/computer-handles.d.ts +79 -0
- package/dist/page/computer-handles.js +44 -0
- package/dist/page/gill-observation.d.ts +4 -0
- package/dist/page/gill-observation.js +31 -0
- package/dist/page/lifetime.d.ts +6 -0
- package/dist/page/lifetime.js +19 -0
- package/dist/page/mount.d.ts +163 -0
- package/dist/page/mount.js +390 -0
- package/dist/page/storage.d.ts +3 -0
- package/dist/page/storage.js +82 -0
- package/dist/page/terminal.d.ts +5 -0
- package/dist/page/terminal.js +7 -0
- package/dist/page/window-launch.d.ts +18 -0
- package/dist/page/window-launch.js +11 -0
- package/dist/page-script.d.ts +14 -0
- package/dist/page-script.js +5 -0
- package/dist/r2-files.d.ts +3 -0
- package/dist/r2-files.js +79 -0
- package/dist/release.d.ts +83 -0
- package/dist/release.js +167 -0
- package/dist/root-data.d.ts +86 -0
- package/dist/root-data.js +69 -0
- package/dist/shell/app-directory.d.ts +542 -0
- package/dist/shell/app-directory.js +199 -0
- package/dist/shell/browser-actions.d.ts +7 -0
- package/dist/shell/browser-actions.js +56 -0
- package/dist/shell/command-host.d.ts +112 -0
- package/dist/shell/command-host.js +502 -0
- package/dist/shell/commands.d.ts +25 -0
- package/dist/shell/commands.js +78 -0
- package/dist/shell/index.d.ts +8 -0
- package/dist/shell/index.js +8 -0
- package/dist/shell/keys.d.ts +41 -0
- package/dist/shell/keys.js +134 -0
- package/dist/shell/shell-module.d.ts +55 -0
- package/dist/shell/shell-module.js +86 -0
- package/dist/shell/window-manager.d.ts +124 -0
- package/dist/shell/window-manager.js +711 -0
- package/dist/shell/workbench-contract.d.ts +68 -0
- package/dist/shell/workbench-contract.js +1 -0
- package/dist/shell/workbench-directory.d.ts +332 -0
- package/dist/shell/workbench-directory.js +35 -0
- package/dist/shell/workbench-host-types.d.ts +96 -0
- package/dist/shell/workbench-host-types.js +1 -0
- package/dist/shell/workbench-host.d.ts +34 -0
- package/dist/shell/workbench-host.js +347 -0
- package/dist/shell/workbench-model.d.ts +192 -0
- package/dist/shell/workbench-model.js +53 -0
- package/dist/shell/workbench.d.ts +4 -0
- package/dist/shell/workbench.js +2 -0
- package/dist/source/fork.d.ts +27 -0
- package/dist/source/fork.js +46 -0
- package/dist/source/index.d.ts +10 -0
- package/dist/source/index.js +10 -0
- package/dist/source/seed-receipt.d.ts +53 -0
- package/dist/source/seed-receipt.js +18 -0
- package/dist/source/team-source.d.ts +32 -0
- package/dist/source/team-source.js +42 -0
- package/dist/source/template-push.d.ts +33 -0
- package/dist/source/template-push.js +223 -0
- package/dist/source/template-repository.d.ts +4 -0
- package/dist/source/template-repository.js +21 -0
- package/dist/source/template-seed.d.ts +68 -0
- package/dist/source/template-seed.js +80 -0
- package/dist/standalone/agent-directory.d.ts +4 -0
- package/dist/standalone/agent-directory.js +4 -0
- package/dist/standalone/agents.d.ts +220 -0
- package/dist/standalone/agents.js +795 -0
- package/dist/standalone/app-builds-binding.d.ts +36 -0
- package/dist/standalone/app-builds-binding.js +119 -0
- package/dist/standalone/app-set.d.ts +91 -0
- package/dist/standalone/app-set.js +395 -0
- package/dist/standalone/app-source.d.ts +41 -0
- package/dist/standalone/app-source.js +59 -0
- package/dist/standalone/builds.d.ts +18 -0
- package/dist/standalone/builds.js +101 -0
- package/dist/standalone/computer-egress.d.ts +37 -0
- package/dist/standalone/computer-egress.js +111 -0
- package/dist/standalone/computer.d.ts +122 -0
- package/dist/standalone/computer.js +714 -0
- package/dist/standalone/install-apps-cli.d.ts +2 -0
- package/dist/standalone/install-apps-cli.js +126 -0
- package/dist/standalone/instance.d.ts +35 -0
- package/dist/standalone/instance.js +74 -0
- package/dist/standalone/media.d.ts +76 -0
- package/dist/standalone/media.js +227 -0
- package/dist/standalone/preview.d.ts +51 -0
- package/dist/standalone/preview.js +167 -0
- package/dist/standalone/registry.d.ts +102 -0
- package/dist/standalone/registry.js +193 -0
- package/dist/standalone/tenancy.d.ts +42 -0
- package/dist/standalone/tenancy.js +241 -0
- package/dist/standalone/worker.d.ts +55 -0
- package/dist/standalone/worker.js +147 -0
- package/dist/standalone/workflow-authority-readers.d.ts +16 -0
- package/dist/standalone/workflow-authority-readers.js +35 -0
- package/dist/standalone/workflow-binding.d.ts +216 -0
- package/dist/standalone/workflow-binding.js +217 -0
- package/dist/standalone/workflow-events.d.ts +46 -0
- package/dist/standalone/workflow-events.js +39 -0
- package/dist/standalone/workflow-git-capture.d.ts +7 -0
- package/dist/standalone/workflow-git-capture.js +62 -0
- package/dist/standalone/workflow-host.d.ts +931 -0
- package/dist/standalone/workflow-host.js +751 -0
- package/dist/standalone/workflow-node-capture.d.ts +13 -0
- package/dist/standalone/workflow-node-capture.js +72 -0
- package/dist/standalone/workflow-root-authority.d.ts +27 -0
- package/dist/standalone/workflow-root-authority.js +87 -0
- package/dist/standalone/workflow-schedules.d.ts +52 -0
- package/dist/standalone/workflow-schedules.js +40 -0
- package/dist/standalone/workflow-trigger-host.d.ts +1694 -0
- package/dist/standalone/workflow-trigger-host.js +52 -0
- package/dist/standalone/workflows.d.ts +657 -0
- package/dist/standalone/workflows.js +554 -0
- package/dist/storage-frame.d.ts +64 -0
- package/dist/storage-frame.js +28 -0
- package/dist/tenancy/auth0.d.ts +64 -0
- package/dist/tenancy/auth0.js +198 -0
- package/dist/tenancy/cloudflare/rate-limit.d.ts +8 -0
- package/dist/tenancy/cloudflare/rate-limit.js +19 -0
- package/dist/tenancy/cloudflare/registry.d.ts +50 -0
- package/dist/tenancy/cloudflare/registry.js +301 -0
- package/dist/tenancy/cloudflare.d.ts +2 -0
- package/dist/tenancy/cloudflare.js +2 -0
- package/dist/tenancy/cold-directory.d.ts +13 -0
- package/dist/tenancy/cold-directory.js +43 -0
- package/dist/tenancy/contracts.d.ts +195 -0
- package/dist/tenancy/contracts.js +1 -0
- package/dist/tenancy/index.d.ts +19 -0
- package/dist/tenancy/index.js +156 -0
- package/dist/tenancy/owner-key.d.ts +26 -0
- package/dist/tenancy/owner-key.js +63 -0
- package/dist/tenancy/oyster.d.ts +4 -0
- package/dist/tenancy/oyster.js +25 -0
- package/dist/tenancy/registry-record.d.ts +7 -0
- package/dist/tenancy/registry-record.js +44 -0
- package/dist/tenancy/registry.d.ts +27 -0
- package/dist/tenancy/registry.js +138 -0
- package/dist/tenancy/resolve.d.ts +7 -0
- package/dist/tenancy/resolve.js +64 -0
- package/dist/tenant.d.ts +81 -0
- package/dist/tenant.js +71 -0
- package/dist/testing/capabilities.d.ts +29 -0
- package/dist/testing/capabilities.js +130 -0
- package/dist/testing/catalog.d.ts +3 -0
- package/dist/testing/catalog.js +53 -0
- package/dist/testing/index.d.ts +10 -0
- package/dist/testing/index.js +10 -0
- package/dist/testing/installation.d.ts +11 -0
- package/dist/testing/installation.js +25 -0
- package/dist/testing/native-worker.d.ts +21 -0
- package/dist/testing/native-worker.js +106 -0
- package/dist/testing/worker.d.ts +18 -0
- package/dist/testing/worker.js +67 -0
- package/dist/testing/workers-runtime.d.ts +24 -0
- package/dist/testing/workers-runtime.js +33 -0
- package/dist/webhook-delivery.d.ts +14 -0
- package/dist/webhook-delivery.js +17 -0
- package/dist/workers-git.d.ts +285 -0
- package/dist/workers-git.js +1174 -0
- package/dist/workers.d.ts +352 -0
- package/dist/workers.js +868 -0
- package/docs/api/README.md +82 -0
- package/docs/api/agents.md +332 -0
- package/docs/api/app-storage.md +209 -0
- package/docs/api/apps.md +1375 -0
- package/docs/api/auth.md +63 -0
- package/docs/api/build-contract.md +1304 -0
- package/docs/api/build.md +1712 -0
- package/docs/api/cloudflare.md +421 -0
- package/docs/api/computer.md +203 -0
- package/docs/api/files.md +117 -0
- package/docs/api/git.md +470 -0
- package/docs/api/host.md +1328 -0
- package/docs/api/index.md +430 -0
- package/docs/api/invocation.md +738 -0
- package/docs/api/keys.md +175 -0
- package/docs/api/mcp.md +301 -0
- package/docs/api/media.md +40 -0
- package/docs/api/modules.md +50 -0
- package/docs/api/pack.md +141 -0
- package/docs/api/page/mount.md +298 -0
- package/docs/api/root-data.md +111 -0
- package/docs/api/shell/app-directory.md +599 -0
- package/docs/api/shell/command-host.md +224 -0
- package/docs/api/shell/commands.md +197 -0
- package/docs/api/shell/keys.md +183 -0
- package/docs/api/shell/shell-module.md +204 -0
- package/docs/api/shell/window-manager.md +267 -0
- package/docs/api/shell/workbench-directory.md +119 -0
- package/docs/api/shell/workbench.md +205 -0
- package/docs/api/shell.md +1915 -0
- package/docs/api/tenancy/auth0.md +110 -0
- package/docs/api/tenancy/cloudflare.md +221 -0
- package/docs/api/tenancy/cold-directory.md +46 -0
- package/docs/api/tenancy/owner-key.md +63 -0
- package/docs/api/tenancy/registry.md +77 -0
- package/docs/api/tenancy/resolve.md +69 -0
- package/docs/api/tenancy.md +430 -0
- package/docs/api/tenant.md +194 -0
- package/docs/api/workers-git.md +1646 -0
- package/docs/api/workers.md +1691 -0
- package/docs/site/agent-quickstart.mdx +346 -0
- package/examples/standalone/worker.ts +7 -0
- package/examples/standalone/wrangler.jsonc +82 -0
- package/package.json +300 -3
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
import { assetBuildPolicySchema, assetBaseSchema, } from "@fungi.computer/puffball/assets";
|
|
2
|
+
import { boundedBundlePolicy, bundledHtmlRecipe } from "./html-recipe.js";
|
|
3
|
+
import { workspaceBuildPolicy } from "./build-policy.js";
|
|
4
|
+
import * as crypto from "node:crypto";
|
|
5
|
+
import { backendJsonSchema, durableBackendSchema, } from "@fungi.computer/puffball/backend";
|
|
6
|
+
import { parseSha256Digest, } from "@fungi.computer/puffball";
|
|
7
|
+
import { appIdSchema } from "@fungi.computer/puffball/effect";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Schema from "effect/Schema";
|
|
10
|
+
const relativeEntrySchema = Schema.String.pipe(Schema.check(Schema.isMinLength(1)), Schema.check(Schema.makeFilter((entry) => {
|
|
11
|
+
if (entry.startsWith("/") || entry.includes("\\"))
|
|
12
|
+
return false;
|
|
13
|
+
return entry
|
|
14
|
+
.split("/")
|
|
15
|
+
.every((part) => part !== "" && part !== "." && part !== "..");
|
|
16
|
+
})));
|
|
17
|
+
/** Canonical recipe policy shared by core and the local adapter. */
|
|
18
|
+
export const pinnedRecipeSchema = Schema.Struct({
|
|
19
|
+
kind: Schema.optional(Schema.Literals(["bundled-html", "source-html"])),
|
|
20
|
+
entry: relativeEntrySchema,
|
|
21
|
+
assets: Schema.optional(assetBuildPolicySchema),
|
|
22
|
+
assetBase: Schema.optionalKey(assetBaseSchema),
|
|
23
|
+
backend: Schema.Union([
|
|
24
|
+
Schema.Null,
|
|
25
|
+
Schema.Struct({
|
|
26
|
+
entry: Schema.optional(relativeEntrySchema),
|
|
27
|
+
runtime: durableBackendSchema.fields.runtime,
|
|
28
|
+
compatibilityDate: durableBackendSchema.fields.compatibilityDate,
|
|
29
|
+
exportName: durableBackendSchema.fields.exportName,
|
|
30
|
+
}),
|
|
31
|
+
]),
|
|
32
|
+
maxOutputBytes: Schema.Number.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.makeFilter((value) => Number.isFinite(value))), Schema.check(Schema.isGreaterThan(0))),
|
|
33
|
+
});
|
|
34
|
+
/** Core configuration contains only immutable App identity and recipe policy. */
|
|
35
|
+
export const AppPublisherCoreConfigSchema = Schema.Struct({
|
|
36
|
+
appId: appIdSchema,
|
|
37
|
+
recipe: pinnedRecipeSchema,
|
|
38
|
+
});
|
|
39
|
+
const esmToolchain = Object.freeze({ esbuild: "0.28.2" });
|
|
40
|
+
const sourceHtmlToolchain = Object.freeze({
|
|
41
|
+
...esmToolchain,
|
|
42
|
+
node: "24.20.0",
|
|
43
|
+
npm: "11.19.0",
|
|
44
|
+
vite: "8.2.2",
|
|
45
|
+
pluginReact: "6.1.1",
|
|
46
|
+
recipe: "captured-npm-source-html-v1",
|
|
47
|
+
packageResolution: "captured-browser-module-main-v1",
|
|
48
|
+
});
|
|
49
|
+
/**
|
|
50
|
+
* The one pinned toolchain for an App page captured with its pnpm workspace.
|
|
51
|
+
* The App's own manifest declares its page policy and the package graph
|
|
52
|
+
* orders its dependency builds, so both are pinned by the source tree digest.
|
|
53
|
+
*/
|
|
54
|
+
const workspaceHtmlToolchain = Object.freeze({
|
|
55
|
+
node: "24.20.0",
|
|
56
|
+
pnpm: "11.24.0",
|
|
57
|
+
vite: "8.2.2",
|
|
58
|
+
pluginReact: "6.1.1",
|
|
59
|
+
singlefile: "2.3.3",
|
|
60
|
+
react: "18.3.1",
|
|
61
|
+
typescript: "5.9.3",
|
|
62
|
+
tailwindcss: "4.2.1",
|
|
63
|
+
tailwindNode: "4.2.1",
|
|
64
|
+
tailwindOxide: "4.2.1",
|
|
65
|
+
tailwindResolver: "5.24.5",
|
|
66
|
+
typography: "0.5.19",
|
|
67
|
+
buildPolicy: workspaceBuildPolicy,
|
|
68
|
+
recipe: "captured-workspace-html-v1",
|
|
69
|
+
});
|
|
70
|
+
const workspaceHtmlBackendToolchain = Object.freeze({
|
|
71
|
+
...workspaceHtmlToolchain,
|
|
72
|
+
...esmToolchain,
|
|
73
|
+
packageResolution: "captured-browser-module-main-v1",
|
|
74
|
+
});
|
|
75
|
+
/** Package-export conditions used by the pinned Workers builder. */
|
|
76
|
+
export const appBackendExportConditions = Object.freeze([
|
|
77
|
+
"workerd",
|
|
78
|
+
"worker",
|
|
79
|
+
"browser",
|
|
80
|
+
]);
|
|
81
|
+
/** Persisted, fully resolved recipe including its code-owned toolchain. */
|
|
82
|
+
export const capturedPinnedRecipeSchema = Schema.Struct({
|
|
83
|
+
kind: pinnedRecipeSchema.fields.kind,
|
|
84
|
+
bundle: Schema.Literal(true),
|
|
85
|
+
entry: relativeEntrySchema,
|
|
86
|
+
format: Schema.Literals(["html", "esm", "assets"]),
|
|
87
|
+
assets: Schema.optional(assetBuildPolicySchema),
|
|
88
|
+
assetBase: Schema.optionalKey(assetBaseSchema),
|
|
89
|
+
toolchain: Schema.Record(Schema.String, Schema.String),
|
|
90
|
+
maxOutputBytes: Schema.Number.pipe(Schema.check(Schema.isInt()), Schema.check(Schema.isGreaterThan(0))),
|
|
91
|
+
platform: Schema.Literals(["browser", "neutral"]),
|
|
92
|
+
backend: pinnedRecipeSchema.fields.backend,
|
|
93
|
+
target: Schema.Tuple([Schema.Literal("es2022")]),
|
|
94
|
+
});
|
|
95
|
+
/** Closed failure boundary for the four private composition stages. */
|
|
96
|
+
export class AppPublisherError extends Error {
|
|
97
|
+
stage;
|
|
98
|
+
_tag = "AppPublisherError";
|
|
99
|
+
constructor(stage, options) {
|
|
100
|
+
super(`App Publisher ${stage} stage failed`, options);
|
|
101
|
+
this.stage = stage;
|
|
102
|
+
this.name = this._tag;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function sha256Digest(value) {
|
|
106
|
+
return parseSha256Digest(`sha256:${crypto.createHash("sha256").update(value, "utf8").digest("hex")}`);
|
|
107
|
+
}
|
|
108
|
+
/** The toolchain this Publisher pins for a recipe of this kind, entry and backend. */
|
|
109
|
+
function currentToolchain(recipe) {
|
|
110
|
+
if (recipe.kind === "source-html")
|
|
111
|
+
return sourceHtmlToolchain;
|
|
112
|
+
if (recipe.kind === bundledHtmlRecipe.kind)
|
|
113
|
+
return recipe.backend === null
|
|
114
|
+
? bundledHtmlRecipe.toolchain
|
|
115
|
+
: { ...bundledHtmlRecipe.toolchain, ...esmToolchain, pnpm: "11.24.0" };
|
|
116
|
+
if (recipe.entry.endsWith(".html"))
|
|
117
|
+
return recipe.backend === null
|
|
118
|
+
? workspaceHtmlToolchain
|
|
119
|
+
: workspaceHtmlBackendToolchain;
|
|
120
|
+
return esmToolchain;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Whether a pinned recipe pins this Publisher's current toolchain (its build
|
|
124
|
+
* policy included). An executor refuses any other: a recipe pinned under
|
|
125
|
+
* another policy describes a build this code does not perform.
|
|
126
|
+
*/
|
|
127
|
+
export function pinsCurrentToolchain(recipe) {
|
|
128
|
+
const entries = (toolchain) => JSON.stringify(Object.entries(toolchain).sort(([a], [b]) => a.localeCompare(b)));
|
|
129
|
+
return entries(currentToolchain(recipe)) === entries(recipe.toolchain);
|
|
130
|
+
}
|
|
131
|
+
function captureConfig(input) {
|
|
132
|
+
let decoded;
|
|
133
|
+
try {
|
|
134
|
+
decoded = Schema.decodeUnknownSync(AppPublisherCoreConfigSchema)(input);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
throw new AppPublisherError("configuration");
|
|
138
|
+
}
|
|
139
|
+
const bundled = decoded.recipe.kind === bundledHtmlRecipe.kind;
|
|
140
|
+
const sourceHtml = decoded.recipe.kind === "source-html";
|
|
141
|
+
// Any other HTML entry is a page built with its own pnpm workspace; the
|
|
142
|
+
// App's manifest, read from the captured source, declares how.
|
|
143
|
+
const workspaceHtml = decoded.recipe.kind === undefined && decoded.recipe.entry.endsWith(".html");
|
|
144
|
+
const html = workspaceHtml || bundled || sourceHtml;
|
|
145
|
+
if (sourceHtml &&
|
|
146
|
+
(!decoded.recipe.entry.endsWith(".html") ||
|
|
147
|
+
!decoded.recipe.assets ||
|
|
148
|
+
!boundedBundlePolicy(decoded.recipe.assets, decoded.recipe.maxOutputBytes)))
|
|
149
|
+
throw new AppPublisherError("configuration");
|
|
150
|
+
if (bundled) {
|
|
151
|
+
const policy = decoded.recipe.assets;
|
|
152
|
+
if (!policy ||
|
|
153
|
+
!decoded.recipe.entry.endsWith("/index.html") ||
|
|
154
|
+
!boundedBundlePolicy(policy, decoded.recipe.maxOutputBytes))
|
|
155
|
+
throw new AppPublisherError("configuration");
|
|
156
|
+
}
|
|
157
|
+
if ((html &&
|
|
158
|
+
decoded.recipe.backend !== null &&
|
|
159
|
+
decoded.recipe.backend.entry === undefined) ||
|
|
160
|
+
(!html && decoded.recipe.backend?.entry !== undefined))
|
|
161
|
+
throw new AppPublisherError("configuration");
|
|
162
|
+
if (decoded.recipe.assets && !html)
|
|
163
|
+
throw new AppPublisherError("configuration");
|
|
164
|
+
if (Boolean(decoded.recipe.assets) !==
|
|
165
|
+
(decoded.recipe.assetBase !== undefined))
|
|
166
|
+
throw new AppPublisherError("configuration");
|
|
167
|
+
const recipeBase = {
|
|
168
|
+
bundle: true,
|
|
169
|
+
entry: decoded.recipe.entry,
|
|
170
|
+
format: decoded.recipe.assets
|
|
171
|
+
? "assets"
|
|
172
|
+
: workspaceHtml
|
|
173
|
+
? "html"
|
|
174
|
+
: "esm",
|
|
175
|
+
toolchain: currentToolchain(decoded.recipe),
|
|
176
|
+
maxOutputBytes: decoded.recipe.maxOutputBytes,
|
|
177
|
+
platform: decoded.recipe.backend === null
|
|
178
|
+
? "browser"
|
|
179
|
+
: "neutral",
|
|
180
|
+
backend: decoded.recipe.backend,
|
|
181
|
+
target: ["es2022"],
|
|
182
|
+
};
|
|
183
|
+
const resolvedBase = decoded.recipe.kind !== undefined
|
|
184
|
+
? { ...recipeBase, kind: decoded.recipe.kind }
|
|
185
|
+
: recipeBase;
|
|
186
|
+
const recipe = Object.freeze(decoded.recipe.assets && decoded.recipe.assetBase !== undefined
|
|
187
|
+
? {
|
|
188
|
+
...resolvedBase,
|
|
189
|
+
assets: Object.freeze({ ...decoded.recipe.assets }),
|
|
190
|
+
assetBase: decoded.recipe.assetBase,
|
|
191
|
+
}
|
|
192
|
+
: resolvedBase);
|
|
193
|
+
return Object.freeze({
|
|
194
|
+
appId: decoded.appId,
|
|
195
|
+
recipe,
|
|
196
|
+
buildRecipeDigest: digestPinnedRecipe(recipe),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/** Hash every captured build and output-policy input in the Publisher recipe. */
|
|
200
|
+
export function digestPinnedRecipe(recipe) {
|
|
201
|
+
const buildMaterial = {
|
|
202
|
+
bundle: recipe.bundle,
|
|
203
|
+
entry: recipe.entry,
|
|
204
|
+
format: recipe.format,
|
|
205
|
+
toolchain: recipe.toolchain,
|
|
206
|
+
};
|
|
207
|
+
const outputPolicy = {
|
|
208
|
+
maxOutputBytes: recipe.maxOutputBytes,
|
|
209
|
+
backendMinify: recipe.backend !== null,
|
|
210
|
+
platform: recipe.platform,
|
|
211
|
+
backend: recipe.backend,
|
|
212
|
+
target: recipe.target,
|
|
213
|
+
};
|
|
214
|
+
const outputMaterial = recipe.backend === null
|
|
215
|
+
? outputPolicy
|
|
216
|
+
: { ...outputPolicy, conditions: appBackendExportConditions };
|
|
217
|
+
const vite = {
|
|
218
|
+
configFile: false,
|
|
219
|
+
envDir: false,
|
|
220
|
+
envPrefix: [],
|
|
221
|
+
cssCodeSplit: recipe.format === "assets",
|
|
222
|
+
inlineDynamicImports: recipe.format !== "assets",
|
|
223
|
+
assetsInlineLimit: recipe.format === "assets" ? 0 : recipe.maxOutputBytes,
|
|
224
|
+
output: recipe.format === "assets"
|
|
225
|
+
? "immutable-asset-build"
|
|
226
|
+
: "one-complete-html",
|
|
227
|
+
css: "compiled-public-caps-stipe",
|
|
228
|
+
dependencyInputs: "contained-before-probe-typescript-host-tailwind-core-loaders",
|
|
229
|
+
subprocesses: "owned-process-group-term-kill-5-seconds",
|
|
230
|
+
};
|
|
231
|
+
const recipeVite = recipe.assets
|
|
232
|
+
? {
|
|
233
|
+
...vite,
|
|
234
|
+
assetPolicy: recipe.assets,
|
|
235
|
+
base: recipe.assetBase,
|
|
236
|
+
cssUrls: "tailwind-node-4.2.1-after-contained-root-check",
|
|
237
|
+
}
|
|
238
|
+
: vite;
|
|
239
|
+
const recipeMaterial = JSON.stringify(recipe.kind === bundledHtmlRecipe.kind
|
|
240
|
+
? {
|
|
241
|
+
...buildMaterial,
|
|
242
|
+
...outputMaterial,
|
|
243
|
+
bundledHtmlRecipe,
|
|
244
|
+
assetPolicy: recipe.assets,
|
|
245
|
+
base: recipe.assetBase,
|
|
246
|
+
}
|
|
247
|
+
: recipe.format !== "esm"
|
|
248
|
+
? {
|
|
249
|
+
...buildMaterial,
|
|
250
|
+
hydration: {
|
|
251
|
+
frozenLockfile: true,
|
|
252
|
+
ignoreScripts: true,
|
|
253
|
+
ignorePnpmfile: true,
|
|
254
|
+
packageImportMethod: "auto",
|
|
255
|
+
store: "snapshot-local",
|
|
256
|
+
virtualStore: "snapshot-local",
|
|
257
|
+
environment: "tool-path-only-snapshot-home-no-user-or-global-npmrc",
|
|
258
|
+
},
|
|
259
|
+
vite: recipeVite,
|
|
260
|
+
...outputMaterial,
|
|
261
|
+
}
|
|
262
|
+
: { ...buildMaterial, ...outputMaterial });
|
|
263
|
+
return sha256Digest(recipeMaterial);
|
|
264
|
+
}
|
|
265
|
+
/** Decode and normalize Publisher-owned recipe policy for durable seed proof. */
|
|
266
|
+
export function normalizeAppPublisherRecipe(input) {
|
|
267
|
+
const captured = captureConfig(input);
|
|
268
|
+
return Object.freeze({
|
|
269
|
+
recipe: captured.recipe,
|
|
270
|
+
buildRecipeDigest: captured.buildRecipeDigest,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function createReleaseBoundary(catalog, release) {
|
|
274
|
+
return Effect.tryPromise({
|
|
275
|
+
try: () => catalog.createRelease(release),
|
|
276
|
+
catch: () => new AppPublisherError("release"),
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function stageFailure(stage) {
|
|
280
|
+
return new AppPublisherError(stage);
|
|
281
|
+
}
|
|
282
|
+
function prepareUiOutput(output, recipe) {
|
|
283
|
+
switch (output.kind) {
|
|
284
|
+
case "bytes":
|
|
285
|
+
return recipe.format === "html" &&
|
|
286
|
+
output.bytes.byteLength <= recipe.maxOutputBytes
|
|
287
|
+
? {
|
|
288
|
+
content: { kind: "html", bytes: output.bytes },
|
|
289
|
+
mediaType: "text/html; charset=utf-8",
|
|
290
|
+
}
|
|
291
|
+
: undefined;
|
|
292
|
+
case "assets":
|
|
293
|
+
return recipe.format === "assets" && output.base === recipe.assetBase
|
|
294
|
+
? {
|
|
295
|
+
content: output,
|
|
296
|
+
mediaType: "application/vnd.fungi.app-assets+json",
|
|
297
|
+
}
|
|
298
|
+
: undefined;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function validPreparedUiOutput(ui, recipe) {
|
|
302
|
+
switch (ui.content.kind) {
|
|
303
|
+
case "html":
|
|
304
|
+
return (ui.mediaType === "text/html; charset=utf-8" &&
|
|
305
|
+
ui.content.bytes.byteLength <= recipe.maxOutputBytes);
|
|
306
|
+
case "assets":
|
|
307
|
+
return (ui.mediaType === "application/vnd.fungi.app-assets+json" &&
|
|
308
|
+
recipe.assets !== undefined &&
|
|
309
|
+
ui.content.base === recipe.assetBase);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function encodeBackendOutput(bytes, recipe) {
|
|
313
|
+
if (recipe.backend === null)
|
|
314
|
+
return Effect.fail(stageFailure("build"));
|
|
315
|
+
return Schema.encodeEffect(backendJsonSchema)({
|
|
316
|
+
...recipe.backend,
|
|
317
|
+
code: new TextDecoder().decode(bytes),
|
|
318
|
+
}).pipe(Effect.map((encoded) => new TextEncoder().encode(encoded)), Effect.filterOrFail((encoded) => encoded.byteLength <= recipe.maxOutputBytes, () => stageFailure("build")), Effect.mapError(() => stageFailure("build")));
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The largest single object a build under this recipe may upload: no larger
|
|
322
|
+
* than the largest output bound the recipe could assign it under.
|
|
323
|
+
*/
|
|
324
|
+
export function pinnedRecipeObjectBound(recipe) {
|
|
325
|
+
return Math.max(recipe.maxOutputBytes, recipe.assets?.maxObjectBytes ?? 0);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Whether output built elsewhere (a Computer's candidate manifest) is one
|
|
329
|
+
* this recipe could assign: the same surfaces and `maxOutputBytes` bound the
|
|
330
|
+
* preparer applies here. Asset sizes stay the artifact writer's policy.
|
|
331
|
+
*/
|
|
332
|
+
export function fitsPinnedRecipe(output, recipe) {
|
|
333
|
+
const uiFits = (ui) => ui.content.kind === "html"
|
|
334
|
+
? ui.content.byteLength <= recipe.maxOutputBytes
|
|
335
|
+
: recipe.assets !== undefined && ui.content.base === recipe.assetBase;
|
|
336
|
+
const backendFits = (backend) => recipe.backend !== null && backend.byteLength <= recipe.maxOutputBytes;
|
|
337
|
+
switch (output.kind) {
|
|
338
|
+
case "ui":
|
|
339
|
+
return recipe.backend === null && uiFits(output.ui);
|
|
340
|
+
case "backend":
|
|
341
|
+
return backendFits(output.backend);
|
|
342
|
+
case "app":
|
|
343
|
+
return uiFits(output.ui) && backendFits(output.backend);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
function assignAppOutput(built, recipe) {
|
|
347
|
+
switch (built.kind) {
|
|
348
|
+
case "bytes":
|
|
349
|
+
case "assets":
|
|
350
|
+
return assignRawOutput(built, recipe);
|
|
351
|
+
case "app":
|
|
352
|
+
return assignJoinedOutput(built, recipe);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function assignRawOutput(built, recipe) {
|
|
356
|
+
const ui = prepareUiOutput(built, recipe);
|
|
357
|
+
if (recipe.backend === null)
|
|
358
|
+
return ui === undefined
|
|
359
|
+
? Effect.fail(stageFailure("build"))
|
|
360
|
+
: Effect.succeed({ kind: "ui", ui });
|
|
361
|
+
if (built.kind !== "bytes")
|
|
362
|
+
return Effect.fail(stageFailure("build"));
|
|
363
|
+
return encodeBackendOutput(built.bytes, recipe).pipe(Effect.map((backend) => ({ kind: "backend", backend })));
|
|
364
|
+
}
|
|
365
|
+
function assignJoinedOutput(built, recipe) {
|
|
366
|
+
const ui = built.ui;
|
|
367
|
+
if (ui !== null && !validPreparedUiOutput(ui, recipe))
|
|
368
|
+
return Effect.fail(stageFailure("build"));
|
|
369
|
+
if (built.backend === null) {
|
|
370
|
+
if (ui === null || recipe.backend !== null)
|
|
371
|
+
return Effect.fail(stageFailure("build"));
|
|
372
|
+
return Effect.succeed({ kind: "ui", ui });
|
|
373
|
+
}
|
|
374
|
+
return encodeBackendOutput(built.backend, recipe).pipe(Effect.map((backend) => ui === null ? { kind: "backend", backend } : { kind: "app", ui, backend }));
|
|
375
|
+
}
|
|
376
|
+
/** Construct one private App Publisher with copied core configuration and roles. */
|
|
377
|
+
export function createAppCandidatePreparer(options) {
|
|
378
|
+
return createCapturedCandidatePreparer(captureConfig(options.config), options);
|
|
379
|
+
}
|
|
380
|
+
/** Prepare a candidate from a previously captured recipe. */
|
|
381
|
+
export function createCapturedCandidatePreparer(config, options) {
|
|
382
|
+
const { sourceCapture, buildExecutor } = options;
|
|
383
|
+
const prepareCandidate = (releaseId) => Effect.scoped(Effect.gen(function* () {
|
|
384
|
+
const source = yield* sourceCapture
|
|
385
|
+
.capture()
|
|
386
|
+
.pipe(Effect.mapError(() => stageFailure("source")));
|
|
387
|
+
const built = yield* buildExecutor
|
|
388
|
+
.build(source, config.recipe)
|
|
389
|
+
.pipe(Effect.mapError((error) => new AppPublisherError("build", { cause: error })));
|
|
390
|
+
const assigned = yield* assignAppOutput(built, config.recipe);
|
|
391
|
+
const declaration = buildExecutor.declaration === undefined
|
|
392
|
+
? undefined
|
|
393
|
+
: yield* buildExecutor
|
|
394
|
+
.declaration(source, config.recipe)
|
|
395
|
+
.pipe(Effect.mapError((error) => new AppPublisherError("build", { cause: error })));
|
|
396
|
+
const output = declaration === undefined ? assigned : { ...assigned, declaration };
|
|
397
|
+
return {
|
|
398
|
+
appId: config.appId,
|
|
399
|
+
releaseId,
|
|
400
|
+
output,
|
|
401
|
+
provenance: {
|
|
402
|
+
sourceRevision: source.sourceRevision,
|
|
403
|
+
sourceTreeDigest: source.sourceTreeDigest,
|
|
404
|
+
buildRecipeDigest: config.buildRecipeDigest,
|
|
405
|
+
},
|
|
406
|
+
};
|
|
407
|
+
}));
|
|
408
|
+
return Object.freeze({ prepareCandidate });
|
|
409
|
+
}
|
|
410
|
+
/** Persist a prepared build through the native artifact and App Release owners. */
|
|
411
|
+
export function commitPreparedCandidate(prepared, options) {
|
|
412
|
+
return Effect.gen(function* () {
|
|
413
|
+
const stored = yield* options.artifactWriter
|
|
414
|
+
.write(prepared.output)
|
|
415
|
+
.pipe(Effect.mapError(() => stageFailure("artifact")));
|
|
416
|
+
const release = {
|
|
417
|
+
releaseId: prepared.releaseId,
|
|
418
|
+
appId: prepared.appId,
|
|
419
|
+
artifact: {
|
|
420
|
+
artifactRef: stored.artifactRef,
|
|
421
|
+
artifactDigest: stored.artifactDigest,
|
|
422
|
+
provenance: prepared.provenance,
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
return yield* createReleaseBoundary(options.releaseCatalog, release);
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
/** Compose capture, compilation, immutable writing and Release recording. */
|
|
429
|
+
export function createAppPublisher(options) {
|
|
430
|
+
const preparer = createAppCandidatePreparer(options);
|
|
431
|
+
const createCandidate = (releaseId) => preparer.prepareCandidate(releaseId).pipe(Effect.flatMap((prepared) => commitPreparedCandidate(prepared, {
|
|
432
|
+
artifactWriter: options.artifactWriter,
|
|
433
|
+
releaseCatalog: options.releaseCatalog,
|
|
434
|
+
})));
|
|
435
|
+
return Object.freeze({ createCandidate });
|
|
436
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Run a nested repository without inheriting the caller's Git checkout. */
|
|
2
|
+
export declare function repositoryEnvironment(): NodeJS.ProcessEnv;
|
|
3
|
+
/** Git environment carrying host-held request headers (credentials) only in
|
|
4
|
+
* Git's own config environment: never argv, a URL or a trace. */
|
|
5
|
+
export declare function authenticatedGitEnvironment(headers: Readonly<Record<string, string>>): NodeJS.ProcessEnv;
|
|
6
|
+
/** Git environment for one authenticated Artifacts fetch; caller validates token. */
|
|
7
|
+
export declare function artifactsGitEnvironment(token: string): NodeJS.ProcessEnv;
|
|
8
|
+
/** Credential-free private route used by the hosted Publisher Git client. */
|
|
9
|
+
export declare function hostedArtifactsGitRemote(endpoint: string, repository: string): string;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** Run a nested repository without inheriting the caller's Git checkout. */
|
|
2
|
+
export function repositoryEnvironment() {
|
|
3
|
+
return Object.fromEntries(Object.entries(process.env).filter(([name]) => !name.startsWith("GIT_")));
|
|
4
|
+
}
|
|
5
|
+
/** Git environment carrying host-held request headers (credentials) only in
|
|
6
|
+
* Git's own config environment: never argv, a URL or a trace. */
|
|
7
|
+
export function authenticatedGitEnvironment(headers) {
|
|
8
|
+
const excludedEnvironmentNames = new Set([
|
|
9
|
+
"GIT_CURL_VERBOSE",
|
|
10
|
+
"CLOUDFLARE_API_TOKEN",
|
|
11
|
+
"ARTIFACTS_API_TOKEN",
|
|
12
|
+
"ARTIFACTS_REPO_TOKEN",
|
|
13
|
+
"ARTIFACTS_TOKEN",
|
|
14
|
+
]);
|
|
15
|
+
const environment = Object.fromEntries(Object.entries(repositoryEnvironment()).filter(([name]) => !name.startsWith("GIT_TRACE") && !excludedEnvironmentNames.has(name)));
|
|
16
|
+
for (const [name, value] of Object.entries(headers))
|
|
17
|
+
if (!/^[A-Za-z0-9-]+$/u.test(name) || /[\r\n\0]/u.test(value))
|
|
18
|
+
throw new Error("Git request header is invalid");
|
|
19
|
+
// An empty first value resets any inherited extra headers.
|
|
20
|
+
const config = [
|
|
21
|
+
["http.extraHeader", ""],
|
|
22
|
+
...Object.entries(headers).map(([name, value]) => ["http.extraHeader", `${name}: ${value}`]),
|
|
23
|
+
["http.followRedirects", "false"],
|
|
24
|
+
];
|
|
25
|
+
return {
|
|
26
|
+
...environment,
|
|
27
|
+
...Object.fromEntries(config.flatMap(([key, value], index) => [
|
|
28
|
+
[`GIT_CONFIG_KEY_${String(index)}`, key],
|
|
29
|
+
[`GIT_CONFIG_VALUE_${String(index)}`, value],
|
|
30
|
+
])),
|
|
31
|
+
GIT_CONFIG_COUNT: String(config.length),
|
|
32
|
+
GIT_TRACE: "0",
|
|
33
|
+
GIT_TRACE_CURL: "0",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Git environment for one authenticated Artifacts fetch; caller validates token. */
|
|
37
|
+
export function artifactsGitEnvironment(token) {
|
|
38
|
+
return authenticatedGitEnvironment({ Authorization: `Bearer ${token}` });
|
|
39
|
+
}
|
|
40
|
+
/** Credential-free private route used by the hosted Publisher Git client. */
|
|
41
|
+
export function hostedArtifactsGitRemote(endpoint, repository) {
|
|
42
|
+
if (!/^[a-zA-Z0-9._-]{1,128}$/.test(repository))
|
|
43
|
+
throw new Error("Artifacts repository is invalid");
|
|
44
|
+
let base;
|
|
45
|
+
try {
|
|
46
|
+
base = new URL(endpoint);
|
|
47
|
+
}
|
|
48
|
+
catch (cause) {
|
|
49
|
+
throw new Error("Publisher endpoint is invalid", { cause });
|
|
50
|
+
}
|
|
51
|
+
const localHttp = base.protocol === "http:" &&
|
|
52
|
+
(base.hostname === "localhost" || base.hostname === "127.0.0.1");
|
|
53
|
+
if ((base.protocol !== "https:" && !localHttp) ||
|
|
54
|
+
base.username.length > 0 ||
|
|
55
|
+
base.password.length > 0 ||
|
|
56
|
+
base.search.length > 0 ||
|
|
57
|
+
base.hash.length > 0)
|
|
58
|
+
throw new Error("Publisher endpoint is invalid");
|
|
59
|
+
return new URL(`/v1/artifacts/git/${encodeURIComponent(repository)}.git`, base).toString();
|
|
60
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AssetBuildPolicy } from "@fungi.computer/puffball/assets";
|
|
2
|
+
/** Engine/toolchain-independent input: an already bundled, captured directory.
|
|
3
|
+
* The producer owns compilation; Publisher never discovers its build scripts.
|
|
4
|
+
* These ceilings cannot be raised by a publication request.
|
|
5
|
+
*/
|
|
6
|
+
export declare const bundledHtmlRecipe: Readonly<{
|
|
7
|
+
kind: "bundled-html";
|
|
8
|
+
toolchain: Readonly<{
|
|
9
|
+
recipe: "captured-bundled-html-v1";
|
|
10
|
+
}>;
|
|
11
|
+
limits: Readonly<{
|
|
12
|
+
maxManifestBytes: 1048576;
|
|
13
|
+
maxObjects: 1024;
|
|
14
|
+
maxObjectBytes: 16777216;
|
|
15
|
+
maxTotalBytes: 67108864;
|
|
16
|
+
}>;
|
|
17
|
+
extensions: readonly string[];
|
|
18
|
+
}>;
|
|
19
|
+
/** Requests may tighten the catalog bounds, never raise them. */
|
|
20
|
+
export declare function boundedBundlePolicy(policy: AssetBuildPolicy, maxOutputBytes: number): boolean;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Engine/toolchain-independent input: an already bundled, captured directory.
|
|
2
|
+
* The producer owns compilation; Publisher never discovers its build scripts.
|
|
3
|
+
* These ceilings cannot be raised by a publication request.
|
|
4
|
+
*/
|
|
5
|
+
export const bundledHtmlRecipe = Object.freeze({
|
|
6
|
+
kind: "bundled-html",
|
|
7
|
+
toolchain: Object.freeze({ recipe: "captured-bundled-html-v1" }),
|
|
8
|
+
limits: Object.freeze({
|
|
9
|
+
maxManifestBytes: 1_048_576,
|
|
10
|
+
maxObjects: 1024,
|
|
11
|
+
maxObjectBytes: 16_777_216,
|
|
12
|
+
maxTotalBytes: 67_108_864,
|
|
13
|
+
}),
|
|
14
|
+
extensions: Object.freeze([
|
|
15
|
+
".html",
|
|
16
|
+
".js",
|
|
17
|
+
".mjs",
|
|
18
|
+
".css",
|
|
19
|
+
".json",
|
|
20
|
+
".gz",
|
|
21
|
+
".wasm",
|
|
22
|
+
".png",
|
|
23
|
+
".jpg",
|
|
24
|
+
".jpeg",
|
|
25
|
+
".webp",
|
|
26
|
+
".avif",
|
|
27
|
+
".gif",
|
|
28
|
+
".svg",
|
|
29
|
+
".ico",
|
|
30
|
+
".woff",
|
|
31
|
+
".woff2",
|
|
32
|
+
".ttf",
|
|
33
|
+
".otf",
|
|
34
|
+
".mp3",
|
|
35
|
+
".ogg",
|
|
36
|
+
".wav",
|
|
37
|
+
".mp4",
|
|
38
|
+
".webm",
|
|
39
|
+
".txt",
|
|
40
|
+
]),
|
|
41
|
+
});
|
|
42
|
+
/** Requests may tighten the catalog bounds, never raise them. */
|
|
43
|
+
export function boundedBundlePolicy(policy, maxOutputBytes) {
|
|
44
|
+
const limits = bundledHtmlRecipe.limits;
|
|
45
|
+
return (maxOutputBytes <= limits.maxTotalBytes &&
|
|
46
|
+
policy.maxTotalBytes <= maxOutputBytes &&
|
|
47
|
+
policy.maxManifestBytes <= limits.maxManifestBytes &&
|
|
48
|
+
policy.maxObjects <= limits.maxObjects &&
|
|
49
|
+
policy.maxObjectBytes <= limits.maxObjectBytes &&
|
|
50
|
+
policy.maxTotalBytes <= limits.maxTotalBytes);
|
|
51
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type DefinitionEmission } from "./definition-emission.js";
|
|
2
|
+
import { type AssetBuildPolicy } from "@fungi.computer/puffball/assets";
|
|
3
|
+
import { parseSha256Digest, type AppId, type SourceRevision } from "@fungi.computer/puffball";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import * as esbuild from "esbuild";
|
|
6
|
+
import { type AppPublisher, type ArtifactWriter, type BuildExecutor, type CapturedSource, type PinnedRecipe, type ReleaseCreator, type SourceCapture } from "./effect.js";
|
|
7
|
+
/** Local proving adapter; accepted source policy is intentionally fixed to main. */
|
|
8
|
+
export declare function createLocalSourceCapture(repositoryPath: string): SourceCapture;
|
|
9
|
+
/** Capture saved App files for local authoring without modifying the customer's
|
|
10
|
+
* repository. Provenance names the exact temporary Git snapshot, not an existing
|
|
11
|
+
* customer commit. Dependencies and local credentials are not source inputs.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createLocalDirectorySourceCapture(directory: string): SourceCapture;
|
|
14
|
+
/** Capture an exact Artifacts commit through a trusted host's credential-free Git URL. */
|
|
15
|
+
export declare function createArtifactsSourceCapture(options: {
|
|
16
|
+
readonly remote: string;
|
|
17
|
+
readonly token: string;
|
|
18
|
+
readonly sourceRevision: SourceRevision;
|
|
19
|
+
}): SourceCapture;
|
|
20
|
+
/** Capture one explicit committed revision without manufacturing a branch. */
|
|
21
|
+
export declare function createLocalCommitSourceCapture(repositoryPath: string, sourceRevision: SourceRevision): SourceCapture;
|
|
22
|
+
/** A first-party editable source tree ready to seed into an Artifacts template. */
|
|
23
|
+
export type FirstPartyAppSourceDistribution = Readonly<{
|
|
24
|
+
readonly appId: AppId;
|
|
25
|
+
readonly sourceRevision: SourceRevision;
|
|
26
|
+
readonly sourceTreeDigest: ReturnType<typeof parseSha256Digest>;
|
|
27
|
+
readonly appDirectory: string;
|
|
28
|
+
readonly files: readonly Readonly<{
|
|
29
|
+
readonly path: string;
|
|
30
|
+
readonly contents: Uint8Array;
|
|
31
|
+
}>[];
|
|
32
|
+
}>;
|
|
33
|
+
/**
|
|
34
|
+
* Project one pinned first-party App and its pnpm workspace dependency graph
|
|
35
|
+
* without changing upstream files or manufacturing Git history. The caller
|
|
36
|
+
* records the returned provenance on the Artifacts template seed commit.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createFirstPartyAppSourceDistribution(options: {
|
|
39
|
+
readonly appId: string;
|
|
40
|
+
readonly source: CapturedSource;
|
|
41
|
+
readonly entry: string;
|
|
42
|
+
}): Effect.Effect<FirstPartyAppSourceDistribution, unknown>;
|
|
43
|
+
/** How one build executor is composed; never part of a recipe or request. */
|
|
44
|
+
export type LocalBuildExecutorOptions = Readonly<{
|
|
45
|
+
packageStoreDirectory?: string;
|
|
46
|
+
/**
|
|
47
|
+
* `run` executes a dependency's declared `fungi.build.script`. Only the
|
|
48
|
+
* admitted Computer build sets it; every other composition refuses a source
|
|
49
|
+
* that declares one, before installing anything.
|
|
50
|
+
*/
|
|
51
|
+
packageScripts?: "run";
|
|
52
|
+
/** Hears how long the dependency install and the compile each took. */
|
|
53
|
+
onPhase?: (phase: "install" | "build", milliseconds: number) => void;
|
|
54
|
+
}>;
|
|
55
|
+
/** Exact pinned esbuild options shared by local builds and artifact-budget proofs. */
|
|
56
|
+
export declare function localSnapshotBuildOptions(source: CapturedSource, recipe: PinnedRecipe, emission: DefinitionEmission): esbuild.BuildOptions;
|
|
57
|
+
/** Local proving adapter; it builds only the captured extracted snapshot. */
|
|
58
|
+
export declare function createLocalBuildExecutor(options?: LocalBuildExecutorOptions): BuildExecutor;
|
|
59
|
+
/** Local proving adapter; the configured root is private and write-once. */
|
|
60
|
+
export declare function createLocalArtifactWriter(artifactRoot: string, assetPolicy?: AssetBuildPolicy): ArtifactWriter;
|
|
61
|
+
/** Local Git, recipe and artifact directory configuration with a Release catalog. */
|
|
62
|
+
export type LocalAppPublisherOptions = Readonly<{
|
|
63
|
+
readonly config: unknown;
|
|
64
|
+
readonly releaseCatalog: ReleaseCreator;
|
|
65
|
+
}>;
|
|
66
|
+
/** Construct the complete local proving adapter around the provider-neutral core. */
|
|
67
|
+
export declare function createLocalAppPublisher(options: LocalAppPublisherOptions): AppPublisher;
|