@fungi.computer/oyster 0.2.0 → 0.3.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.
Files changed (287) hide show
  1. package/README.md +376 -72
  2. package/dist/agents.d.ts +3 -2
  3. package/dist/agents.js +4 -2
  4. package/dist/app-builds.d.ts +81 -8
  5. package/dist/app-builds.js +210 -60
  6. package/dist/app-storage.d.ts +3 -3
  7. package/dist/approval-capability.d.ts +5 -0
  8. package/dist/approval-capability.js +46 -0
  9. package/dist/approval-home.d.ts +51 -0
  10. package/dist/approval-home.js +226 -0
  11. package/dist/apps/catalog.d.ts +335 -0
  12. package/dist/apps/catalog.js +46 -0
  13. package/dist/apps/gate.d.ts +20 -6
  14. package/dist/apps/gate.js +42 -16
  15. package/dist/apps/index.d.ts +3 -1
  16. package/dist/apps/index.js +3 -1
  17. package/dist/apps/installation.d.ts +132 -51
  18. package/dist/apps/installation.js +176 -70
  19. package/dist/apps/permissions.d.ts +247 -0
  20. package/dist/apps/permissions.js +255 -0
  21. package/dist/boot-capability-route.d.ts +15 -0
  22. package/dist/boot-capability-route.js +196 -0
  23. package/dist/boot-capability.d.ts +103 -0
  24. package/dist/boot-capability.js +141 -0
  25. package/dist/boot-construction.d.ts +12 -0
  26. package/dist/boot-construction.js +175 -0
  27. package/dist/boot-gate.d.ts +267 -0
  28. package/dist/boot-gate.js +236 -0
  29. package/dist/boot-host-script.d.ts +2 -0
  30. package/dist/boot-host-script.js +1 -0
  31. package/dist/boot-mount.d.ts +21 -0
  32. package/dist/boot-mount.js +65 -0
  33. package/dist/browser/boot.d.ts +73 -0
  34. package/dist/browser/boot.js +502 -0
  35. package/dist/browser/browser.d.ts +104 -0
  36. package/dist/browser/browser.js +374 -0
  37. package/dist/browser/capability-source.d.ts +76 -0
  38. package/dist/browser/capability-source.js +338 -0
  39. package/dist/browser/cdp-client.d.ts +56 -0
  40. package/dist/browser/cdp-client.js +184 -0
  41. package/dist/browser/errors.d.ts +79 -0
  42. package/dist/browser/errors.js +32 -0
  43. package/dist/browser/index.d.ts +16 -0
  44. package/dist/browser/index.js +14 -0
  45. package/dist/browser/lease-registry.d.ts +61 -0
  46. package/dist/browser/lease-registry.js +101 -0
  47. package/dist/browser/operations.d.ts +225 -0
  48. package/dist/browser/operations.js +137 -0
  49. package/dist/browser/page.d.ts +25 -0
  50. package/dist/browser/page.js +172 -0
  51. package/dist/browser/quick-reads.d.ts +29 -0
  52. package/dist/browser/quick-reads.js +160 -0
  53. package/dist/browser/sessions.d.ts +52 -0
  54. package/dist/browser/sessions.js +118 -0
  55. package/dist/browser/url-policy.d.ts +18 -0
  56. package/dist/browser/url-policy.js +58 -0
  57. package/dist/build/app-imports.d.ts +12 -0
  58. package/dist/build/app-imports.js +105 -0
  59. package/dist/build/build-policy.d.ts +5 -3
  60. package/dist/build/build-policy.js +5 -3
  61. package/dist/build/candidate-builder.d.ts +1 -1
  62. package/dist/build/candidate-cli.mjs +8375 -334
  63. package/dist/build/local-build.d.ts +2 -0
  64. package/dist/build/local-build.js +28 -6
  65. package/dist/build/snapshot-package-store.d.ts +1 -1
  66. package/dist/build/snapshot-package-store.js +7 -2
  67. package/dist/build/workspace-build.d.ts +92 -5
  68. package/dist/build/workspace-build.js +93 -19
  69. package/dist/build.d.ts +1 -1
  70. package/dist/build.js +1 -1
  71. package/dist/catalog-frame.js +1 -1
  72. package/dist/cloudflare.d.ts +13 -3
  73. package/dist/cloudflare.js +102 -4
  74. package/dist/frame-binding-wire-script.js +1 -1
  75. package/dist/frame-binding-wire.js +4 -2
  76. package/dist/git-auth.d.ts +0 -2
  77. package/dist/git-auth.js +0 -2
  78. package/dist/git.d.ts +0 -1
  79. package/dist/git.js +0 -2
  80. package/dist/host.d.ts +39 -13
  81. package/dist/host.js +268 -142
  82. package/dist/index.d.ts +25 -15
  83. package/dist/index.js +39 -16
  84. package/dist/install-release-cli.d.ts +2 -0
  85. package/dist/install-release-cli.js +46 -0
  86. package/dist/installation-client.d.ts +43 -0
  87. package/dist/installation-client.js +178 -0
  88. package/dist/installation-execution.d.ts +11 -0
  89. package/dist/installation-execution.js +184 -0
  90. package/dist/installation-operations.d.ts +1437 -0
  91. package/dist/installation-operations.js +664 -0
  92. package/dist/installation-store-generation.d.ts +3 -0
  93. package/dist/installation-store-generation.js +16 -0
  94. package/dist/instance-id.d.ts +10 -0
  95. package/dist/instance-id.js +10 -0
  96. package/dist/instance-refusal.d.ts +11 -0
  97. package/dist/instance-refusal.js +21 -0
  98. package/dist/internal/delivery.js +4 -2
  99. package/dist/internal/host-gate.js +7 -1
  100. package/dist/internal/registry-installations.js +4 -4
  101. package/dist/keys.d.ts +5 -1
  102. package/dist/keys.js +10 -2
  103. package/dist/machine-link/authority.d.ts +89 -0
  104. package/dist/machine-link/authority.js +37 -0
  105. package/dist/machine-link/client.d.ts +66 -0
  106. package/dist/machine-link/client.js +94 -0
  107. package/dist/machine-link/delivery.d.ts +18 -0
  108. package/dist/machine-link/delivery.js +55 -0
  109. package/dist/machine-link/enrollment.d.ts +111 -0
  110. package/dist/machine-link/enrollment.js +286 -0
  111. package/dist/machine-link/fences.d.ts +37 -0
  112. package/dist/machine-link/fences.js +166 -0
  113. package/dist/machine-link/home.d.ts +20 -0
  114. package/dist/machine-link/home.js +24 -0
  115. package/dist/machine-link/host.d.ts +43 -0
  116. package/dist/machine-link/host.js +189 -0
  117. package/dist/machine-link/index.d.ts +15 -0
  118. package/dist/machine-link/index.js +15 -0
  119. package/dist/machine-link/link.d.ts +48 -0
  120. package/dist/machine-link/link.js +223 -0
  121. package/dist/machine-link/presence.d.ts +10 -0
  122. package/dist/machine-link/presence.js +25 -0
  123. package/dist/machine-link/workers.d.ts +6 -0
  124. package/dist/machine-link/workers.js +6 -0
  125. package/dist/mcp-owner.d.ts +2 -2
  126. package/dist/mcp-owner.js +41 -33
  127. package/dist/mcp.d.ts +15 -3
  128. package/dist/mcp.js +146 -25
  129. package/dist/pack.d.ts +14 -3
  130. package/dist/pack.js +44 -19
  131. package/dist/page/app-access.d.ts +7 -0
  132. package/dist/page/app-access.js +75 -0
  133. package/dist/page/boot-host.js +57 -0
  134. package/dist/page/capabilities.d.ts +3 -3
  135. package/dist/page/capabilities.js +16 -12
  136. package/dist/page/computer-handles.d.ts +3 -3
  137. package/dist/page/computer-target.d.ts +3 -0
  138. package/dist/page/computer-target.js +9 -0
  139. package/dist/page/mount.d.ts +23 -3
  140. package/dist/page/mount.js +152 -16
  141. package/dist/page-assets.d.ts +22 -0
  142. package/dist/page-assets.js +28 -0
  143. package/dist/page-script.d.ts +3 -0
  144. package/dist/page-script.js +5 -4
  145. package/dist/release.d.ts +13 -6
  146. package/dist/release.js +10 -7
  147. package/dist/root-data.d.ts +16 -6
  148. package/dist/root-data.js +2 -2
  149. package/dist/source/index.d.ts +1 -1
  150. package/dist/source/team-source.d.ts +20 -8
  151. package/dist/source/team-source.js +39 -10
  152. package/dist/source/template-seed.d.ts +2 -2
  153. package/dist/standalone/agent-mail.d.ts +24 -0
  154. package/dist/standalone/agent-mail.js +62 -0
  155. package/dist/standalone/agent-store.d.ts +9 -0
  156. package/dist/standalone/agent-store.js +30 -0
  157. package/dist/standalone/agents.d.ts +68 -9
  158. package/dist/standalone/agents.js +428 -56
  159. package/dist/standalone/app-builds-binding.d.ts +20 -2
  160. package/dist/standalone/app-builds-binding.js +147 -23
  161. package/dist/standalone/app-set.d.ts +5 -2
  162. package/dist/standalone/app-set.js +174 -143
  163. package/dist/standalone/browser.d.ts +11 -0
  164. package/dist/standalone/browser.js +72 -0
  165. package/dist/standalone/builds.d.ts +13 -4
  166. package/dist/standalone/builds.js +28 -44
  167. package/dist/standalone/computer-egress.d.ts +2 -0
  168. package/dist/standalone/computer-egress.js +13 -4
  169. package/dist/standalone/computer-fleet.d.ts +43 -0
  170. package/dist/standalone/computer-fleet.js +200 -0
  171. package/dist/standalone/computer.d.ts +44 -9
  172. package/dist/standalone/computer.js +129 -22
  173. package/dist/standalone/install-apps-cli.js +43 -6
  174. package/dist/standalone/instance.d.ts +2 -2
  175. package/dist/standalone/instance.js +8 -11
  176. package/dist/standalone/machine-link.d.ts +354 -0
  177. package/dist/standalone/machine-link.js +368 -0
  178. package/dist/standalone/media.d.ts +2 -2
  179. package/dist/standalone/media.js +19 -16
  180. package/dist/standalone/preview.d.ts +2 -2
  181. package/dist/standalone/preview.js +37 -10
  182. package/dist/standalone/registry.d.ts +149 -9
  183. package/dist/standalone/registry.js +299 -8
  184. package/dist/standalone/worker.d.ts +19 -5
  185. package/dist/standalone/worker.js +90 -54
  186. package/dist/standalone/workflow-binding.d.ts +18 -22
  187. package/dist/standalone/workflow-binding.js +18 -18
  188. package/dist/standalone/workflow-control.d.ts +4 -0
  189. package/dist/standalone/workflow-control.js +40 -0
  190. package/dist/standalone/workflow-events.d.ts +2 -2
  191. package/dist/standalone/workflow-events.js +9 -10
  192. package/dist/standalone/workflow-git-capture.js +4 -3
  193. package/dist/standalone/workflow-host.d.ts +162 -161
  194. package/dist/standalone/workflow-host.js +101 -21
  195. package/dist/standalone/workflow-node-capture.js +11 -4
  196. package/dist/standalone/workflow-schedules.d.ts +5 -2
  197. package/dist/standalone/workflow-schedules.js +9 -10
  198. package/dist/standalone/workflow-trigger-host.d.ts +65 -5
  199. package/dist/standalone/workflow-trigger-host.js +2 -2
  200. package/dist/standalone/workflow-webhooks.d.ts +22 -0
  201. package/dist/standalone/workflow-webhooks.js +39 -0
  202. package/dist/standalone/workflows.d.ts +140 -43
  203. package/dist/standalone/workflows.js +198 -39
  204. package/dist/storage-frame.d.ts +6 -6
  205. package/dist/testing/capabilities.js +14 -3
  206. package/dist/testing/installation.js +26 -2
  207. package/dist/testing/worker.js +1 -1
  208. package/dist/testing/workers-runtime.d.ts +8 -2
  209. package/dist/testing/workers-runtime.js +20 -2
  210. package/dist/webhook-abuse.d.ts +25 -0
  211. package/dist/webhook-abuse.js +54 -0
  212. package/dist/webhook-custody.d.ts +144 -0
  213. package/dist/webhook-custody.js +222 -0
  214. package/dist/webhook-ingress.d.ts +36 -0
  215. package/dist/webhook-ingress.js +146 -0
  216. package/dist/webhooks.d.ts +111 -0
  217. package/dist/webhooks.js +189 -0
  218. package/dist/workers-git.d.ts +12 -17
  219. package/dist/workers-git.js +60 -52
  220. package/dist/workers.d.ts +224 -13
  221. package/dist/workers.js +351 -51
  222. package/dist/workflow-agent-authority.d.ts +8 -0
  223. package/dist/workflow-agent-authority.js +42 -0
  224. package/dist/workflows.d.ts +66 -0
  225. package/dist/workflows.js +97 -0
  226. package/docs/api/README.md +63 -37
  227. package/docs/api/agents.md +17 -0
  228. package/docs/api/app-storage.md +1 -1
  229. package/docs/api/apps.md +1394 -543
  230. package/docs/api/browser/boot.md +132 -0
  231. package/docs/api/browser.md +1440 -0
  232. package/docs/api/build-contract.md +38 -25
  233. package/docs/api/build.md +263 -84
  234. package/docs/api/cloudflare.md +93 -7
  235. package/docs/api/git.md +3 -13
  236. package/docs/api/host.md +944 -118
  237. package/docs/api/index.md +320 -18
  238. package/docs/api/keys.md +24 -1
  239. package/docs/api/mcp.md +59 -12
  240. package/docs/api/modules.md +33 -40
  241. package/docs/api/pack.md +57 -8
  242. package/docs/api/page/mount.md +92 -4
  243. package/docs/api/workers-git.md +103 -24
  244. package/docs/api/workers.md +1581 -244
  245. package/docs/site/agent-quickstart.mdx +28 -14
  246. package/examples/standalone/worker.ts +3 -1
  247. package/examples/standalone/wrangler.jsonc +2 -0
  248. package/package.json +73 -71
  249. package/dist/installation-consent.d.ts +0 -81
  250. package/dist/installation-consent.js +0 -84
  251. package/dist/shell/app-directory.d.ts +0 -542
  252. package/dist/shell/app-directory.js +0 -196
  253. package/dist/shell/browser-actions.d.ts +0 -7
  254. package/dist/shell/browser-actions.js +0 -56
  255. package/dist/shell/command-host.d.ts +0 -112
  256. package/dist/shell/command-host.js +0 -502
  257. package/dist/shell/commands.d.ts +0 -25
  258. package/dist/shell/commands.js +0 -78
  259. package/dist/shell/index.d.ts +0 -8
  260. package/dist/shell/index.js +0 -8
  261. package/dist/shell/keys.d.ts +0 -41
  262. package/dist/shell/keys.js +0 -134
  263. package/dist/shell/shell-module.d.ts +0 -55
  264. package/dist/shell/shell-module.js +0 -86
  265. package/dist/shell/window-manager.d.ts +0 -128
  266. package/dist/shell/window-manager.js +0 -730
  267. package/dist/shell/workbench-contract.d.ts +0 -68
  268. package/dist/shell/workbench-directory.d.ts +0 -332
  269. package/dist/shell/workbench-directory.js +0 -35
  270. package/dist/shell/workbench-host-types.d.ts +0 -96
  271. package/dist/shell/workbench-host-types.js +0 -1
  272. package/dist/shell/workbench-host.d.ts +0 -34
  273. package/dist/shell/workbench-host.js +0 -352
  274. package/dist/shell/workbench-model.d.ts +0 -194
  275. package/dist/shell/workbench-model.js +0 -53
  276. package/dist/shell/workbench.d.ts +0 -4
  277. package/dist/shell/workbench.js +0 -2
  278. package/docs/api/shell/app-directory.md +0 -599
  279. package/docs/api/shell/command-host.md +0 -224
  280. package/docs/api/shell/commands.md +0 -197
  281. package/docs/api/shell/keys.md +0 -183
  282. package/docs/api/shell/shell-module.md +0 -204
  283. package/docs/api/shell/window-manager.md +0 -267
  284. package/docs/api/shell/workbench-directory.md +0 -119
  285. package/docs/api/shell/workbench.md +0 -205
  286. package/docs/api/shell.md +0 -1915
  287. /package/dist/{shell/workbench-contract.js → page/boot-host.d.ts} +0 -0
@@ -43,6 +43,8 @@ export declare function createFirstPartyAppSourceDistribution(options: {
43
43
  /** How one build executor is composed; never part of a recipe or request. */
44
44
  export type LocalBuildExecutorOptions = Readonly<{
45
45
  packageStoreDirectory?: string;
46
+ /** Host-hydrated metadata; supplied only for a cache-only pnpm install. */
47
+ packageCacheDirectory?: string;
46
48
  /**
47
49
  * `run` executes a dependency's declared `fungi.build.script`. Only the
48
50
  * admitted Computer build sets it; every other composition refuses a source
@@ -11,7 +11,7 @@ import { frozenSnapshotInstallArguments, packageScriptArguments, } from "./snaps
11
11
  import { isInsideSnapshot } from "./snapshot-programs/snapshot-path.js";
12
12
  import { writeImmutableArtifact, } from "./artifact-writer.js";
13
13
  import { assetPathSchema, } from "@fungi.computer/puffball/assets";
14
- import { resolveWorkspaceBuild, workspaceProjectsArguments, } from "./workspace-build.js";
14
+ import { appBuildRecipe, readBuildManifest, resolveWorkspaceBuild, workspaceProjectsArguments, } from "./workspace-build.js";
15
15
  import { readBundledHtml } from "./bundled-html.js";
16
16
  import { createHash, randomBytes } from "node:crypto";
17
17
  import { spawn } from "node:child_process";
@@ -280,9 +280,14 @@ function productionWorkspaceSelectors(build) {
280
280
  return ["--filter-prod", `${build.packageName}...`];
281
281
  }
282
282
  /** Derive the App's page build from the captured tree's own manifests. */
283
- function capturedWorkspaceBuild(snapshot, entry, run) {
283
+ function capturedWorkspaceBuild(snapshot, entry, run, backendEntry) {
284
284
  return Effect.flatMap(run(workspaceProjectsArguments), (projects) => Effect.tryPromise({
285
- try: () => resolveWorkspaceBuild({ root: snapshot, entry, projects }),
285
+ try: () => resolveWorkspaceBuild({
286
+ root: snapshot,
287
+ entry,
288
+ projects,
289
+ backendEntry,
290
+ }),
286
291
  catch: (error) => error,
287
292
  }));
288
293
  }
@@ -487,6 +492,23 @@ export function createFirstPartyAppSourceDistribution(options) {
487
492
  await collect(relativePath);
488
493
  for (const relativePath of sourceDistributionRootFiles)
489
494
  await collect(relativePath);
495
+ // The projected repository is one App, even though its source keeps
496
+ // workspace-relative paths. Preserve npm metadata and publish that
497
+ // App's current identity/recipe at the root registration reads.
498
+ const jsonObject = Schema.Record(Schema.String, Schema.Json);
499
+ const rootPackage = Schema.decodeUnknownSync(Schema.fromJsonString(jsonObject))(await readFile(join(snapshot, "package.json"), "utf8"));
500
+ const rootFungi = rootPackage.fungi === undefined
501
+ ? {}
502
+ : Schema.decodeUnknownSync(jsonObject)(rootPackage.fungi);
503
+ const manifest = readBuildManifest(await readFile(join(snapshot, appDirectory, "package.json"), "utf8"));
504
+ collected.set("package.json", Buffer.from(JSON.stringify({
505
+ ...rootPackage,
506
+ fungi: {
507
+ ...rootFungi,
508
+ appId,
509
+ build: appBuildRecipe(manifest, appDirectory),
510
+ },
511
+ }, null, 2)));
490
512
  await collect("patches");
491
513
  if (collected.has("pnpm-lock.yaml"))
492
514
  collected.set("pnpm-lock.yaml", await prunePnpmLockfile(snapshot, (importerPath) => collected.has(join(importerPath, "package.json"))));
@@ -617,7 +639,7 @@ function rebuildViteSnapshot(sourceSnapshotPath, recipe, options) {
617
639
  if ((yield* run(["--version"])).trim() !== "11.24.0") {
618
640
  return yield* Effect.fail(new Error("Publisher requires pnpm 11.24.0"));
619
641
  }
620
- const html = yield* capturedWorkspaceBuild(sourceSnapshotPath, recipe.entry, run);
642
+ const html = yield* capturedWorkspaceBuild(sourceSnapshotPath, recipe.entry, run, recipe.backend?.entry ?? null);
621
643
  yield* Effect.tryPromise({
622
644
  try: () => checkPinnedWorkspaceToolchain(sourceSnapshotPath, html, recipe),
623
645
  catch: (error) => error,
@@ -628,7 +650,7 @@ function rebuildViteSnapshot(sourceSnapshotPath, recipe, options) {
628
650
  const installStarted = performance.now();
629
651
  yield* run([
630
652
  ...productionWorkspaceSelectors(html),
631
- ...frozenSnapshotInstallArguments(options.packageStoreDirectory),
653
+ ...frozenSnapshotInstallArguments(options.packageStoreDirectory, options.packageCacheDirectory),
632
654
  ]);
633
655
  const buildStarted = performance.now();
634
656
  options.onPhase?.("install", buildStarted - installStarted);
@@ -962,7 +984,7 @@ export function createLocalBuildExecutor(options = {}) {
962
984
  const environment = snapshotBuildEnvironment(source.snapshotPath);
963
985
  if ((yield* runHtmlCommand(["--version"], source.snapshotPath, environment)).trim() !== "11.24.0")
964
986
  return yield* Effect.fail(new Error("Publisher requires pnpm 11.24.0"));
965
- yield* runHtmlCommand(frozenSnapshotInstallArguments(options.packageStoreDirectory), source.snapshotPath, environment);
987
+ yield* runHtmlCommand(frozenSnapshotInstallArguments(options.packageStoreDirectory, options.packageCacheDirectory), source.snapshotPath, environment);
966
988
  }
967
989
  }
968
990
  const ui = yield* recipe.kind === "bundled-html"
@@ -1,5 +1,5 @@
1
1
  /** Frozen hydration policy. Cached bytes are verified and copied into each snapshot. */
2
- export declare function frozenSnapshotInstallArguments(packageStoreDirectory?: string): string[];
2
+ export declare function frozenSnapshotInstallArguments(packageStoreDirectory?: string, packageCacheDirectory?: string): string[];
3
3
  /**
4
4
  * `pnpm run` for one declared package build script, which follows `--` so
5
5
  * pnpm selects that literal name (never an option or a `/pattern/`). pnpm 11
@@ -1,9 +1,11 @@
1
1
  import { isAbsolute } from "node:path";
2
2
  /** Frozen hydration policy. Cached bytes are verified and copied into each snapshot. */
3
- export function frozenSnapshotInstallArguments(packageStoreDirectory) {
3
+ export function frozenSnapshotInstallArguments(packageStoreDirectory, packageCacheDirectory) {
4
4
  if (packageStoreDirectory !== undefined && !isAbsolute(packageStoreDirectory))
5
5
  throw new Error("Publisher package store must be an absolute host path");
6
- return [
6
+ if (packageCacheDirectory !== undefined && !isAbsolute(packageCacheDirectory))
7
+ throw new Error("Publisher package cache must be an absolute host path");
8
+ const args = [
7
9
  "install",
8
10
  "--prod=false",
9
11
  "--frozen-lockfile",
@@ -18,6 +20,9 @@ export function frozenSnapshotInstallArguments(packageStoreDirectory) {
18
20
  "--config.enable-global-virtual-store=false",
19
21
  "--reporter=append-only",
20
22
  ];
23
+ if (packageCacheDirectory !== undefined)
24
+ args.push("--offline", `--cache-dir=${packageCacheDirectory}`);
25
+ return args;
21
26
  }
22
27
  /**
23
28
  * `pnpm run` for one declared package build script, which follows `--` so
@@ -1,18 +1,31 @@
1
1
  import * as Schema from "effect/Schema";
2
+ import { type AppPublisherCoreConfigInput } from "./effect.js";
2
3
  import type { WorkspaceBuildStep } from "./build-step.js";
3
4
  /**
4
5
  * `package.json#fungi.build`. An App manifest (the manifest nearest the
5
- * entry) declares how its page builds: `react`, `shellEntry`, the workspace
6
+ * entry) declares its recipe beside the page policy: `react`, `shellEntry`, the workspace
6
7
  * packages whose declared stylesheets compile first (`packageStylesheets`)
7
8
  * and its own `stylesheets`, compiled in place, in order. A workspace
8
9
  * package declares its build products: extra TypeScript configs compiled
9
10
  * after its `tsconfig.build.json`, and its stylesheet input and output; or,
10
11
  * instead of TypeScript configs, the one `package.json` script that builds
11
12
  * it (`script`), which only an admitted Computer build runs. Every path is
12
- * relative to the manifest that declares it. Unknown build fields are
13
+ * relative to the manifest that declares it. Dependency-only metadata needs
14
+ * no App recipe; App callers require one through appBuildRecipe. Unknown fields are
13
15
  * refused, so a builder never silently ignores a policy it does not know.
14
16
  */
15
17
  export declare const workspaceBuildPolicySchema: Schema.Struct<{
18
+ readonly entry: Schema.optional<Schema.String>;
19
+ readonly backend: Schema.optional<Schema.Union<readonly [Schema.Null, Schema.Struct<{
20
+ readonly entry: Schema.optional<Schema.String>;
21
+ readonly runtime: Schema.Struct<{
22
+ readonly kind: Schema.Literal<"durable-facet">;
23
+ readonly stateSchema: Schema.Number;
24
+ }>;
25
+ readonly compatibilityDate: Schema.String;
26
+ readonly exportName: Schema.String;
27
+ }>]>>;
28
+ readonly maxOutputBytes: Schema.optional<Schema.Number>;
16
29
  readonly react: Schema.optional<Schema.Boolean>;
17
30
  readonly shellEntry: Schema.optional<Schema.String>;
18
31
  readonly packageStylesheets: Schema.optional<Schema.$Array<Schema.String>>;
@@ -24,11 +37,81 @@ export declare const workspaceBuildPolicySchema: Schema.Struct<{
24
37
  readonly input: Schema.String;
25
38
  readonly output: Schema.String;
26
39
  }>>;
40
+ readonly kind: Schema.optional<Schema.Literals<readonly ["bundled-html", "source-html"]>>;
41
+ readonly assets: Schema.optional<Schema.Struct<{
42
+ readonly maxManifestBytes: Schema.Number;
43
+ readonly maxObjects: Schema.Number;
44
+ readonly maxObjectBytes: Schema.Number;
45
+ readonly maxTotalBytes: Schema.Number;
46
+ }>>;
47
+ readonly assetBase: Schema.optionalKey<Schema.String>;
27
48
  }>;
28
49
  /** The build policy one manifest declares. */
29
50
  export type WorkspaceBuildPolicy = Schema.Schema.Type<typeof workspaceBuildPolicySchema>;
51
+ /** Read package metadata once; only fungi.build rejects unknown fields. */
52
+ export declare function readBuildManifest(packageJson: string): {
53
+ fungi: {
54
+ build: {
55
+ readonly kind?: "bundled-html" | "source-html" | undefined;
56
+ readonly backend?: {
57
+ readonly runtime: {
58
+ readonly kind: "durable-facet";
59
+ readonly stateSchema: number;
60
+ };
61
+ readonly compatibilityDate: string;
62
+ readonly exportName: string;
63
+ readonly entry?: string | undefined;
64
+ } | null | undefined;
65
+ readonly assets?: {
66
+ readonly maxManifestBytes: number;
67
+ readonly maxObjects: number;
68
+ readonly maxObjectBytes: number;
69
+ readonly maxTotalBytes: number;
70
+ } | undefined;
71
+ readonly stylesheet?: {
72
+ readonly input: string;
73
+ readonly output: string;
74
+ } | undefined;
75
+ readonly script?: string | undefined;
76
+ readonly entry?: string | undefined;
77
+ readonly assetBase?: string | undefined;
78
+ readonly maxOutputBytes?: number | undefined;
79
+ readonly react?: boolean | undefined;
80
+ readonly typescript?: readonly string[] | undefined;
81
+ readonly shellEntry?: string | undefined;
82
+ readonly packageStylesheets?: readonly string[] | undefined;
83
+ readonly stylesheets?: readonly string[] | undefined;
84
+ } | undefined;
85
+ appId?: import("@fungi.computer/puffball").AppId | undefined;
86
+ };
87
+ name?: string | undefined;
88
+ dependencies?: {
89
+ readonly [x: string]: string;
90
+ } | undefined;
91
+ scripts?: {
92
+ readonly [x: string]: string;
93
+ } | undefined;
94
+ };
95
+ /** Manifest metadata with the App-owned build policy decoded. */
96
+ export type BuildManifest = ReturnType<typeof readBuildManifest>;
97
+ /** Select an App recipe and root its paths in the captured source tree. */
98
+ export declare function appBuildRecipe(manifest: BuildManifest, directory: string): AppPublisherCoreConfigInput["recipe"];
30
99
  /** The pnpm arguments that list a source tree's workspace projects. */
31
100
  export declare const workspaceProjectsArguments: readonly string[];
101
+ /** A nested package must own its build graph, never inherit its host's. */
102
+ export declare class WorkspaceBuildError extends Error {
103
+ /** The nearer package, relative to the captured source root. */
104
+ readonly directory: string;
105
+ /** The workspace whose dependencies would otherwise be inherited. */
106
+ readonly ownerDirectory: string;
107
+ /** The nearest package is absent from pnpm's workspace inventory. */
108
+ readonly code = "non_workspace_package";
109
+ constructor(
110
+ /** The nearer package, relative to the captured source root. */
111
+ directory: string,
112
+ /** The workspace whose dependencies would otherwise be inherited. */
113
+ ownerDirectory: string);
114
+ }
32
115
  /** One App page build derived from its manifests and package graph. */
33
116
  export type WorkspaceBuild = Readonly<{
34
117
  /** The pnpm workspace that owns the entry, relative to the source root. */
@@ -37,19 +120,23 @@ export type WorkspaceBuild = Readonly<{
37
120
  react: boolean;
38
121
  /** A shell module entry, relative to `directory`. */
39
122
  shellEntry?: string;
40
- /** The workspace's runtime workspace dependencies, dependencies first. */
123
+ /** Compile dependencies of the App's runtime imports, dependencies first. */
41
124
  dependencies: readonly string[];
42
125
  steps: readonly WorkspaceBuildStep[];
43
126
  }>;
44
127
  /**
45
128
  * Derive one App's page build from the captured source alone: the manifest
46
- * nearest `entry` declares the App policy, the owning pnpm workspace's
47
- * runtime `workspace:` dependencies give the compile order, and each
129
+ * nearest `entry` must be the owning pnpm workspace (or standalone repository
130
+ * root). It declares the App policy, its
131
+ * runtime imports select its compile roots; their `workspace:` dependencies
132
+ * give the compile order, and each
48
133
  * dependency's manifest declares its own extra build products.
49
134
  */
50
135
  export declare function resolveWorkspaceBuild(options: {
51
136
  readonly root: string;
52
137
  readonly entry: string;
138
+ /** The selected recipe's backend, if it differs from the manifest recipe. */
139
+ readonly backendEntry?: string | null | undefined;
53
140
  /** `pnpm ls -r --depth -1 --json` output for the same source tree. */
54
141
  readonly projects: string;
55
142
  }): Promise<WorkspaceBuild>;
@@ -1,25 +1,28 @@
1
1
  import { access, readFile } from "node:fs/promises";
2
2
  import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
3
3
  import * as Schema from "effect/Schema";
4
- const manifestPathSchema = Schema.String.pipe(Schema.check(Schema.makeFilter((path) => path.length > 0 &&
5
- !path.startsWith("/") &&
6
- !path.includes("\\") &&
7
- path
8
- .split("/")
9
- .every((part) => part !== "" && part !== "." && part !== ".."))));
4
+ import { appIdSchema } from "@fungi.computer/puffball/effect";
5
+ import { pinnedRecipeSchema, } from "./effect.js";
6
+ import { appRuntimeImports } from "./app-imports.js";
7
+ const manifestPathSchema = pinnedRecipeSchema.fields.entry;
10
8
  /**
11
9
  * `package.json#fungi.build`. An App manifest (the manifest nearest the
12
- * entry) declares how its page builds: `react`, `shellEntry`, the workspace
10
+ * entry) declares its recipe beside the page policy: `react`, `shellEntry`, the workspace
13
11
  * packages whose declared stylesheets compile first (`packageStylesheets`)
14
12
  * and its own `stylesheets`, compiled in place, in order. A workspace
15
13
  * package declares its build products: extra TypeScript configs compiled
16
14
  * after its `tsconfig.build.json`, and its stylesheet input and output; or,
17
15
  * instead of TypeScript configs, the one `package.json` script that builds
18
16
  * it (`script`), which only an admitted Computer build runs. Every path is
19
- * relative to the manifest that declares it. Unknown build fields are
17
+ * relative to the manifest that declares it. Dependency-only metadata needs
18
+ * no App recipe; App callers require one through appBuildRecipe. Unknown fields are
20
19
  * refused, so a builder never silently ignores a policy it does not know.
21
20
  */
22
21
  export const workspaceBuildPolicySchema = Schema.Struct({
22
+ ...pinnedRecipeSchema.fields,
23
+ entry: Schema.optional(pinnedRecipeSchema.fields.entry),
24
+ backend: Schema.optional(pinnedRecipeSchema.fields.backend),
25
+ maxOutputBytes: Schema.optional(pinnedRecipeSchema.fields.maxOutputBytes),
23
26
  react: Schema.optional(Schema.Boolean),
24
27
  shellEntry: Schema.optional(manifestPathSchema),
25
28
  packageStylesheets: Schema.optional(Schema.Array(Schema.String)),
@@ -33,16 +36,45 @@ const manifestSchema = Schema.fromJsonString(Schema.Struct({
33
36
  name: Schema.optional(Schema.String),
34
37
  dependencies: Schema.optional(Schema.Record(Schema.String, Schema.String)),
35
38
  scripts: Schema.optional(Schema.Record(Schema.String, Schema.String)),
36
- fungi: Schema.optional(Schema.Struct({ build: Schema.optional(Schema.Unknown) })),
39
+ fungi: Schema.optional(Schema.Struct({
40
+ appId: Schema.optional(appIdSchema),
41
+ build: Schema.optional(Schema.Unknown),
42
+ })),
37
43
  }));
38
- /** A manifest's build policy; only `fungi.build` itself is held strict. */
39
- function policyOf(manifest) {
44
+ /** Read package metadata once; only fungi.build rejects unknown fields. */
45
+ export function readBuildManifest(packageJson) {
46
+ const manifest = Schema.decodeUnknownSync(manifestSchema)(packageJson);
40
47
  const build = manifest.fungi?.build;
41
- return build === undefined
42
- ? {}
43
- : Schema.decodeUnknownSync(workspaceBuildPolicySchema, {
44
- onExcessProperty: "error",
45
- })(build);
48
+ return {
49
+ ...manifest,
50
+ fungi: {
51
+ ...manifest.fungi,
52
+ build: build === undefined
53
+ ? undefined
54
+ : Schema.decodeUnknownSync(workspaceBuildPolicySchema, {
55
+ onExcessProperty: "error",
56
+ })(build),
57
+ },
58
+ };
59
+ }
60
+ /** Select an App recipe and root its paths in the captured source tree. */
61
+ export function appBuildRecipe(manifest, directory) {
62
+ const recipe = Schema.decodeUnknownSync(pinnedRecipeSchema)(manifest.fungi.build);
63
+ if (directory === "")
64
+ return Schema.encodeSync(pinnedRecipeSchema)(recipe);
65
+ const root = Schema.decodeUnknownSync(manifestPathSchema)(directory);
66
+ const backend = recipe.backend;
67
+ return Schema.encodeSync(pinnedRecipeSchema)({
68
+ ...recipe,
69
+ entry: root + "/" + recipe.entry,
70
+ backend: backend?.entry === undefined
71
+ ? backend
72
+ : { ...backend, entry: root + "/" + backend.entry },
73
+ });
74
+ }
75
+ /** The same policy drives dependency products and the App page. */
76
+ function policyOf(manifest) {
77
+ return manifest.fungi.build ?? {};
46
78
  }
47
79
  /** One pnpm workspace project, as `pnpm ls -r --depth -1 --json` lists it. */
48
80
  const workspaceProjectsSchema = Schema.fromJsonString(Schema.Array(Schema.Struct({
@@ -57,6 +89,23 @@ export const workspaceProjectsArguments = Object.freeze([
57
89
  "-1",
58
90
  "--json",
59
91
  ]);
92
+ /** A nested package must own its build graph, never inherit its host's. */
93
+ export class WorkspaceBuildError extends Error {
94
+ directory;
95
+ ownerDirectory;
96
+ /** The nearest package is absent from pnpm's workspace inventory. */
97
+ code = "non_workspace_package";
98
+ constructor(
99
+ /** The nearer package, relative to the captured source root. */
100
+ directory,
101
+ /** The workspace whose dependencies would otherwise be inherited. */
102
+ ownerDirectory) {
103
+ super(`${directory} has its own package.json but is not a workspace member; refusing to inherit ${ownerDirectory || "the repository root"}'s build dependencies`);
104
+ this.directory = directory;
105
+ this.ownerDirectory = ownerDirectory;
106
+ this.name = "WorkspaceBuildError";
107
+ }
108
+ }
60
109
  function within(root, path) {
61
110
  const relativePath = relative(root, resolve(root, path));
62
111
  if (relativePath === ".." ||
@@ -70,8 +119,10 @@ async function exists(path) {
70
119
  }
71
120
  /**
72
121
  * Derive one App's page build from the captured source alone: the manifest
73
- * nearest `entry` declares the App policy, the owning pnpm workspace's
74
- * runtime `workspace:` dependencies give the compile order, and each
122
+ * nearest `entry` must be the owning pnpm workspace (or standalone repository
123
+ * root). It declares the App policy, its
124
+ * runtime imports select its compile roots; their `workspace:` dependencies
125
+ * give the compile order, and each
75
126
  * dependency's manifest declares its own extra build products.
76
127
  */
77
128
  export async function resolveWorkspaceBuild(options) {
@@ -82,7 +133,7 @@ export async function resolveWorkspaceBuild(options) {
82
133
  const known = manifests.get(directory);
83
134
  if (known)
84
135
  return known;
85
- const decoded = Schema.decodeUnknownSync(manifestSchema)(await readFile(join(root, directory, "package.json"), "utf8"));
136
+ const decoded = readBuildManifest(await readFile(join(root, directory, "package.json"), "utf8"));
86
137
  manifests.set(directory, decoded);
87
138
  return decoded;
88
139
  };
@@ -118,15 +169,38 @@ export async function resolveWorkspaceBuild(options) {
118
169
  while (appDirectory !== directory &&
119
170
  !(await exists(join(root, appDirectory, "package.json"))))
120
171
  appDirectory = parent(appDirectory);
172
+ if (appDirectory !== directory)
173
+ throw new WorkspaceBuildError(appDirectory, directory);
121
174
  const policy = policyOf(await manifest(appDirectory));
122
175
  if (policy.script !== undefined)
123
176
  throw new Error(`${appDirectory} is the App; its page builds with the recipe, not a script`);
177
+ const runtimeImports = await appRuntimeImports({
178
+ root,
179
+ entry,
180
+ backendEntry: options.backendEntry === undefined
181
+ ? policy.backend?.entry === undefined
182
+ ? undefined
183
+ : join(appDirectory, policy.backend.entry)
184
+ : (options.backendEntry ?? undefined),
185
+ shellEntry: policy.shellEntry === undefined
186
+ ? undefined
187
+ : join(appDirectory, policy.shellEntry),
188
+ });
189
+ const imported = (name) => runtimeImports.has(name) ||
190
+ [...runtimeImports].some((path) => path.startsWith(`${name}/`));
124
191
  const dependencies = [];
125
192
  const visiting = new Set([directory]);
126
193
  const visit = async (from) => {
127
194
  for (const [name, range] of Object.entries((await manifest(from)).dependencies ?? {})) {
128
195
  if (!range.startsWith("workspace:"))
129
196
  continue;
197
+ // A package still builds its complete declared products. Only the App
198
+ // roots are narrowed: erased types and authoring-only imports do not
199
+ // compile the host runtime. Explicit stylesheet products remain roots.
200
+ if (from === directory &&
201
+ !imported(name) &&
202
+ !policy.packageStylesheets?.includes(name))
203
+ continue;
130
204
  const dependency = projects.get(name);
131
205
  if (dependency === undefined)
132
206
  throw new Error(`${from} depends on an unlisted workspace ${name}`);
package/dist/build.d.ts CHANGED
@@ -3,7 +3,7 @@ export * from "./build/local-build.js";
3
3
  export { createDefinitionEmission, type AppBackendOutput, type DefinitionEmission, } from "./build/definition-emission.js";
4
4
  export { readBundledHtml } from "./build/bundled-html.js";
5
5
  export { bundledHtmlRecipe, boundedBundlePolicy } from "./build/html-recipe.js";
6
- export { resolveWorkspaceBuild, workspaceBuildPolicySchema, workspaceProjectsArguments, type WorkspaceBuild, type WorkspaceBuildPolicy, } from "./build/workspace-build.js";
6
+ export { resolveWorkspaceBuild, readBuildManifest, appBuildRecipe, type BuildManifest, WorkspaceBuildError, workspaceBuildPolicySchema, workspaceProjectsArguments, type WorkspaceBuild, type WorkspaceBuildPolicy, } from "./build/workspace-build.js";
7
7
  export { workspaceBuildStepSchema, type WorkspaceBuildStep, } from "./build/build-step.js";
8
8
  export { frozenSnapshotInstallArguments } from "./build/snapshot-package-store.js";
9
9
  export { repositoryEnvironment, artifactsGitEnvironment, hostedArtifactsGitRemote, } from "./build/git-environment.js";
package/dist/build.js CHANGED
@@ -5,7 +5,7 @@ export * from "./build/local-build.js";
5
5
  export { createDefinitionEmission, } from "./build/definition-emission.js";
6
6
  export { readBundledHtml } from "./build/bundled-html.js";
7
7
  export { bundledHtmlRecipe, boundedBundlePolicy } from "./build/html-recipe.js";
8
- export { resolveWorkspaceBuild, workspaceBuildPolicySchema, workspaceProjectsArguments, } from "./build/workspace-build.js";
8
+ export { resolveWorkspaceBuild, readBuildManifest, appBuildRecipe, WorkspaceBuildError, workspaceBuildPolicySchema, workspaceProjectsArguments, } from "./build/workspace-build.js";
9
9
  export { workspaceBuildStepSchema, } from "./build/build-step.js";
10
10
  export { frozenSnapshotInstallArguments } from "./build/snapshot-package-store.js";
11
11
  export { repositoryEnvironment, artifactsGitEnvironment, hostedArtifactsGitRemote, } from "./build/git-environment.js";
@@ -1,6 +1,6 @@
1
1
  import * as Schema from "effect/Schema";
2
2
  import { appIdSchema } from "@fungi.computer/puffball/effect";
3
- import { appCatalogResponseSchema, } from "./shell/app-directory.js";
3
+ import { appCatalogResponseSchema, } from "@fungi.computer/nacre/app-directory";
4
4
  /** Guest input for `APPS.fork`: the platform App to fork and a chosen label. */
5
5
  export const appForkInputSchema = Schema.Struct({
6
6
  platformAppId: appIdSchema,
@@ -4,12 +4,18 @@ export type { BackgroundFrameState } from "./invocation.js";
4
4
  import { WorkerEntrypoint } from "cloudflare:workers";
5
5
  import { type GuestLimits } from "./guest-policy.js";
6
6
  export type { GuestLimits } from "./guest-policy.js";
7
+ import type { ArtifactHeads, ArtifactStore, ArtifactStreams } from "./release.js";
7
8
  import { type AppBackendBindingName } from "@fungi.computer/puffball/app";
8
9
  import { type InvocationFrame, type InvocationGate } from "./invocation.js";
9
10
  import * as Schema from "effect/Schema";
10
11
  import { z } from "zod";
12
+ import type { Release } from "@fungi.computer/puffball";
13
+ import { type UiBootCheckOutcome } from "./boot-gate.js";
14
+ export { checkBackendConstruction } from "./boot-construction.js";
11
15
  import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
12
- import { type AppInstance, type InstancePorts } from "./index.js";
16
+ import { AppInstanceError, type AppInstance, type InstancePorts } from "./index.js";
17
+ /** Store Release artifacts without loading the Worker ownership barrel. */
18
+ export declare function r2ArtifactStore(bucket: R2Bucket): ArtifactStore & ArtifactHeads & ArtifactStreams;
13
19
  /** Encode supervisor refusals before native RPC can discard Error properties. */
14
20
  export declare function captureFrameBindingReply<Value>(run: () => Promise<Value>): Promise<FrameBindingReply<Value>>;
15
21
  /** Retained shard reply ceiling; replies settle before the frame aborts its facet. */
@@ -50,7 +56,7 @@ export declare const backgroundFrameMethods: {
50
56
  };
51
57
  /** The shared RPC boundary decoder for host-minted invocation binding props. */
52
58
  export declare const frameBindingPropsSchema: Schema.Struct<{
53
- readonly instanceId: Schema.Codec<import("./index.js").InstanceId, string, never, never>;
59
+ readonly instanceId: Schema.Codec<import("./instance-id.js").InstanceId, string, never, never>;
54
60
  readonly frameId: Schema.String;
55
61
  readonly binding: Schema.refine<"BACKGROUND" | "GIT" | "EVENTS" | "APPS" | "WORKFLOWS" | "APP_STORAGE" | "MEDIA" | "COMPUTER" | "INBOX" | "AGENTS", Schema.String>;
56
62
  }>;
@@ -85,6 +91,8 @@ export type AppInstanceContext<FacetClass> = Readonly<{
85
91
  abort: (name: string, reason: Error) => void;
86
92
  /** Delete the named facet and its storage. */
87
93
  delete: (name: string) => void;
94
+ /** Native same-supervisor copy; absence refuses initialized-state probes. */
95
+ clone?: (source: string, destination: string) => void;
88
96
  }>;
89
97
  }>;
90
98
  /** The module set the Worker Loader runs for one App backend. */
@@ -118,6 +126,8 @@ export declare function createCloudflareInvocationGate<Host, FacetClass>(ctx: Pi
118
126
  * @param loader - The Worker Loader binding; without it backend execution is unavailable.
119
127
  * @param resolve - Resolves a Release ID to its verified durable backend.
120
128
  * @param guestEnv - Mints loopbacks bound to the captured invocation frame.
129
+ * @param bootUi - Host-owned UI check, or `null` when this executor deliberately skips it (for example, after the signed host already checked).
121
130
  * @param limits - Guest CPU and subrequest budgets; omitted fields use safe defaults.
131
+ * @param onAbort - Close the host's existing invocation frame after its state is fenced.
122
132
  */
123
- export declare function createCloudflareAppInstance<FacetClass, Host>(ctx: AppInstanceContext<FacetClass>, loader: AppInstanceLoader<FacetClass> | undefined, resolve: InstancePorts<Host>["resolve"], guestEnv: (frame: InvocationFrame<Host>) => object, limits?: Partial<GuestLimits>): AppInstance<Host>;
133
+ export declare function createCloudflareAppInstance<FacetClass, Host>(ctx: AppInstanceContext<FacetClass>, loader: AppInstanceLoader<FacetClass> | undefined, resolve: InstancePorts<Host>["resolve"], guestEnv: (frame: InvocationFrame<Host>) => object, bootUi: (release: Release) => Promise<UiBootCheckOutcome | null>, limits?: Partial<GuestLimits>, onAbort?: (reason: AppInstanceError) => Promise<void>): AppInstance<Host>;