@inkandswitch/patchwork 0.0.1 → 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/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # @inkandswitch/patchwork
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0aa315d: Configure Subduction or Keyhive with exclusive `syncServers` configuration. `syncServers.keyhive` replaces the `keyhive` and `keyhiveSyncServer` site options and the runtime `setup({ keyhive })` option. Selecting a named ARK relay or providing a custom relay identity and URL enables Keyhive, and configured server URLs now control worker connections as well as connection hints.
8
+ - bd63259: New package: one import for a Patchwork site. It owns the boot sequence (`repo`, `router`, `loading`), the vite plugin (config, html, importmap, manifest, netlify, icons, service worker), the site-kit config helpers, and the ambient client types — all previously spread across the bootloader and each site's own `index.html`, `vite.config.ts`, and `public/` directory.
9
+
10
+ A site is now a `package.json` dependency, a `vite.config.ts` with `patchwork({...})`, and a `main.ts` that imports `@inkandswitch/patchwork`.
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [0aa315d]
15
+ - Updated dependencies [bd63259]
16
+ - Updated dependencies [bd63259]
17
+ - Updated dependencies [bd63259]
18
+ - @inkandswitch/patchwork-bootloader@0.5.0
19
+ - @inkandswitch/patchwork-elements@4.0.2
20
+ - @inkandswitch/patchwork-filesystem@0.2.3
package/dist/client.d.ts CHANGED
@@ -13,8 +13,6 @@ import "@inkandswitch/patchwork-providers";
13
13
 
14
14
  declare global {
15
15
  const __SITE_NAME__: string;
16
- const __KEYHIVE__: boolean;
17
- const __KEYHIVE_SYNC_SERVER__: boolean;
18
16
 
19
17
  interface ImportMetaEnv {
20
18
  /**
package/dist/index.js CHANGED
@@ -84,7 +84,7 @@ async function doSetup(options) {
84
84
  }
85
85
  });
86
86
  let workerAdapter = new MessageChannelNetworkAdapter(workerPort);
87
- ({ repo, hive, signerIdentity } = await createRepo(options, siteName, workerAdapter));
87
+ ({ repo, hive, signerIdentity } = await createRepo(siteName, workerAdapter));
88
88
  // The worker was recreated with cold state: wire the repo onto the fresh
89
89
  // port and drop the adapter stranded on the dead one.
90
90
  const bootHive = hive;
package/dist/repo.d.ts CHANGED
@@ -3,9 +3,7 @@ import { type AutomergeRepoKeyhive } from "@automerge/automerge-repo-keyhive";
3
3
  import setupServiceWorker from "@inkandswitch/patchwork-bootloader";
4
4
  import type { SignerIdentity } from "./types.js";
5
5
  export declare function initWasm(): Promise<void>;
6
- export declare function createRepo(config: {
7
- keyhive?: boolean;
8
- }, siteName: string, workerAdapter: MessageChannelNetworkAdapter): Promise<{
6
+ export declare function createRepo(siteName: string, workerAdapter: MessageChannelNetworkAdapter): Promise<{
9
7
  repo: Repo;
10
8
  hive?: AutomergeRepoKeyhive;
11
9
  signerIdentity?: SignerIdentity;
package/dist/repo.js CHANGED
@@ -7,7 +7,9 @@ import { initSync as initSubductionSync } from "@automerge/automerge-subduction/
7
7
  import { MemorySigner } from "@automerge/automerge-subduction/slim";
8
8
  import debug from "debug";
9
9
  const log = debug("patchwork:setup:repo");
10
- const useKeyhiveSyncServer = typeof __KEYHIVE_SYNC_SERVER__ !== "undefined" && __KEYHIVE_SYNC_SERVER__;
10
+ const syncServer = typeof __SYNC_SERVER__ !== "undefined"
11
+ ? __SYNC_SERVER__
12
+ : { url: "wss://subduction.sync.inkandswitch.com" };
11
13
  // Fetch and initialize automerge + subduction wasm. Memoized: the fetches start
12
14
  // on the first call and every later caller awaits the same init. Skipped
13
15
  // entirely when the site brings its own Repo (it did this itself).
@@ -25,8 +27,8 @@ export function initWasm() {
25
27
  }
26
28
  return wasmReady;
27
29
  }
28
- export async function createRepo(config, siteName, workerAdapter) {
29
- if (config.keyhive) {
30
+ export async function createRepo(siteName, workerAdapter) {
31
+ if (syncServer.keyhive) {
30
32
  log("setting up keyhive");
31
33
  initKeyhiveWasm();
32
34
  const { hive, repo } = await initializeAutomergeRepoKeyhiveWithRepo({
@@ -38,7 +40,7 @@ export async function createRepo(config, siteName, workerAdapter) {
38
40
  cachingMode: "periodic",
39
41
  onlyShareWithHardcodedServerPeerId: false,
40
42
  // ARK selects the relay via `syncServer`, defaulting to "subduction".
41
- ...(useKeyhiveSyncServer ? { syncServer: "keyhive" } : {}),
43
+ syncServer: syncServer.keyhive,
42
44
  repo: {
43
45
  storage: new IndexedDBWorkerStorageAdapter(),
44
46
  enableRemoteHeadsGossiping: true,
@@ -7,7 +7,7 @@
7
7
  * wires these into vite's plugin hooks (dev middleware, emitFile, virtual
8
8
  * modules, config()).
9
9
  */
10
- export type { PatchworkSiteOptions, PatchworkIconsOptions, PatchworkHtmlOptions, PatchworkNetlifyOptions, PatchworkSyncServersOptions, } from "./options.js";
10
+ export type { PatchworkSiteOptions, PatchworkIconsOptions, PatchworkHtmlOptions, PatchworkNetlifyOptions, PatchworkKeyhiveSyncServer, PatchworkSyncServersOptions, } from "./options.js";
11
11
  export { resolveSyncServers, PRELOAD_WASM_ASSETS } from "./sync-servers.js";
12
12
  export { getIcons, ICON_SPECS, type IconSpec } from "./icons.js";
13
13
  export { buildHtml, escapeHtml } from "./html.js";
@@ -1,3 +1,4 @@
1
+ import type { SyncServerIdentity } from "@automerge/automerge-repo-keyhive";
1
2
  /**
2
3
  * The bundler-agnostic option shape for a Patchwork site's generated
3
4
  * static assets (icons, index.html, manifest.webmanifest, Netlify config).
@@ -22,14 +23,20 @@ export interface PatchworkNetlifyOptions {
22
23
  /** Cache-Control: immutable on /assets/*. Default true. */
23
24
  immutableAssets?: boolean;
24
25
  }
25
- export interface PatchworkSyncServersOptions {
26
+ type PatchworkPrimarySyncServerOptions = {
27
+ subduction?: string;
28
+ keyhive?: never;
29
+ } | {
30
+ subduction?: never;
31
+ keyhive: PatchworkKeyhiveSyncServer;
32
+ };
33
+ export type PatchworkKeyhiveSyncServer = "keyhive" | "subduction" | ({
34
+ url: string;
35
+ } & SyncServerIdentity);
36
+ export type PatchworkSyncServersOptions = {
26
37
  /** wss:// URL for the legacy automerge-repo sync-server channel (connected on demand via connectClassicSync). Default: wss://sync3.automerge.org. Pass false to skip its preconnect hint. */
27
38
  classic?: string | false;
28
- /** wss:// URL for subduction sync — live when keyhiveSyncServer is false. Default: wss://subduction.sync.inkandswitch.com */
29
- subduction?: string;
30
- /** wss:// URL for keyhive sync — live when keyhiveSyncServer is true. Default: wss://keyhive.sync.automerge.org */
31
- keyhive?: string;
32
- }
39
+ } & PatchworkPrimarySyncServerOptions;
33
40
  export interface PatchworkSiteOptions {
34
41
  /** -> __SITE_NAME__ define */
35
42
  siteName?: string;
@@ -41,10 +48,6 @@ export interface PatchworkSiteOptions {
41
48
  description?: string;
42
49
  /** default "/src/main.ts" — must be root-absolute, since the generated index.html doesn't live at the project root */
43
50
  entry?: string;
44
- /** -> __KEYHIVE__ define */
45
- keyhive?: boolean;
46
- /** -> __KEYHIVE_SYNC_SERVER__ define */
47
- keyhiveSyncServer?: boolean;
48
51
  themeColor?: string | {
49
52
  light: string;
50
53
  dark: string;
@@ -52,12 +55,12 @@ export interface PatchworkSiteOptions {
52
55
  /** manifest background_color */
53
56
  backgroundColor?: string;
54
57
  /**
55
- * Which sync-server hosts to preconnect/dns-prefetch from the html and
56
- * list in the Netlify Link header. Only the channel actually live for
57
- * this build is included subduction xor keyhive, picked by
58
- * `keyhiveSyncServer`, matching automerge-worker.ts's own selection
59
- * plus `classic` (on-demand, but cheap to hint) unless set to `false`.
60
- * Pass `false` to skip all sync-server hints.
58
+ * Sync-server configuration for this build. Providing `keyhive` enables
59
+ * keyhive and selects the relay identity ARK grants access to. Custom
60
+ * identities also require their WebSocket URL. `subduction` and `keyhive`
61
+ * are mutually exclusive. The live server and `classic` are also emitted as
62
+ * connection hints. Pass `false` to keep the default servers but skip those
63
+ * hints.
61
64
  */
62
65
  syncServers?: false | PatchworkSyncServersOptions;
63
66
  icons?: false | PatchworkIconsOptions;
@@ -65,3 +68,4 @@ export interface PatchworkSiteOptions {
65
68
  manifest?: false | Record<string, unknown>;
66
69
  netlify?: false | PatchworkNetlifyOptions;
67
70
  }
71
+ export {};
@@ -1,9 +1 @@
1
- /**
2
- * The bundler-agnostic option shape for a Patchwork site's generated
3
- * static assets (icons, index.html, manifest.webmanifest, Netlify config).
4
- * `../vite/patchwork-plugin.ts` extends this with vite-only config
5
- * (importmap, server/preview/worker/build) — a different bundler adapter
6
- * can reuse this same shape and the pure builders in this directory without
7
- * pulling in anything vite-specific.
8
- */
9
1
  export {};
@@ -1,11 +1,18 @@
1
1
  import type { PatchworkSiteOptions } from "./options.js";
2
+ import type { SyncServerSelection } from "@automerge/automerge-repo-keyhive";
3
+ export declare const DEFAULT_SYNC_SERVERS: {
4
+ classic: string;
5
+ subduction: string;
6
+ keyhive: string;
7
+ };
8
+ export declare function resolvePrimarySyncServer(options: PatchworkSiteOptions): {
9
+ url: string;
10
+ keyhive?: SyncServerSelection;
11
+ };
2
12
  /**
3
13
  * Resolves which sync-server origins are actually live for this build: the
4
- * channel that's live is subduction xor keyhive (picked by
5
- * `keyhiveSyncServer`, matching automerge-worker.ts's own selection) plus
6
- * classic (on-demand, but still worth a preconnect hint) unless disabled.
7
- * A flat list would drift from `keyhiveSyncServer` — e.g. always hinting
8
- * subduction even on a build that actually connects to keyhive.
14
+ * channel that's live is subduction xor keyhive plus classic (on-demand,
15
+ * but still worth a preconnect hint) unless disabled.
9
16
  */
10
17
  export declare function resolveSyncServers(options: PatchworkSiteOptions): string[];
11
18
  export declare const PRELOAD_WASM_ASSETS: string[];
@@ -2,33 +2,46 @@
2
2
  // and automerge-worker.ts's SUBDUCTION_SYNC_URL selection — kept here as
3
3
  // plain constants (rather than importing those runtime modules) since this
4
4
  // only needs the hostnames, not the browser-only logic that reads them.
5
- const DEFAULT_SYNC_SERVERS = {
5
+ export const DEFAULT_SYNC_SERVERS = {
6
6
  classic: "wss://sync3.automerge.org",
7
7
  subduction: "wss://subduction.sync.inkandswitch.com",
8
8
  keyhive: "wss://keyhive.sync.automerge.org",
9
9
  };
10
+ export function resolvePrimarySyncServer(options) {
11
+ const servers = options.syncServers || undefined;
12
+ if (servers?.keyhive) {
13
+ if (typeof servers.keyhive === "string") {
14
+ return {
15
+ keyhive: servers.keyhive,
16
+ url: DEFAULT_SYNC_SERVERS[servers.keyhive],
17
+ };
18
+ }
19
+ const { url, ...identity } = servers.keyhive;
20
+ return {
21
+ keyhive: identity,
22
+ url,
23
+ };
24
+ }
25
+ return {
26
+ url: servers?.subduction ?? DEFAULT_SYNC_SERVERS.subduction,
27
+ };
28
+ }
10
29
  function wsToHttpOrigin(wsUrl) {
11
30
  return wsUrl.replace(/^ws/, "http");
12
31
  }
13
32
  /**
14
33
  * Resolves which sync-server origins are actually live for this build: the
15
- * channel that's live is subduction xor keyhive (picked by
16
- * `keyhiveSyncServer`, matching automerge-worker.ts's own selection) plus
17
- * classic (on-demand, but still worth a preconnect hint) unless disabled.
18
- * A flat list would drift from `keyhiveSyncServer` — e.g. always hinting
19
- * subduction even on a build that actually connects to keyhive.
34
+ * channel that's live is subduction xor keyhive plus classic (on-demand,
35
+ * but still worth a preconnect hint) unless disabled.
20
36
  */
21
37
  export function resolveSyncServers(options) {
22
38
  if (options.syncServers === false)
23
39
  return [];
24
- const servers = { ...DEFAULT_SYNC_SERVERS, ...options.syncServers };
25
- const origins = [];
26
- const primary = options.keyhiveSyncServer
27
- ? servers.keyhive
28
- : servers.subduction;
29
- origins.push(primary);
30
- if (servers.classic)
31
- origins.push(servers.classic);
40
+ const primary = resolvePrimarySyncServer(options);
41
+ const classic = options.syncServers?.classic ?? DEFAULT_SYNC_SERVERS.classic;
42
+ const origins = [primary.url];
43
+ if (classic)
44
+ origins.push(classic);
32
45
  return origins.map(wsToHttpOrigin);
33
46
  }
34
47
  // Emitted by importmap-plugin.ts. keyhive_wasm.wasm is loaded lazily (only
package/dist/types.d.ts CHANGED
@@ -57,11 +57,6 @@ export interface PatchworkOptions {
57
57
  name?: string;
58
58
  /** DOM id of the `<patchwork-view>` hosting the root tool. Defaults to "root". */
59
59
  rootElementId?: string;
60
- /**
61
- * Initialize keyhive for access control. The Repo then uses keyhive's network
62
- * adapter, peerId and idFactory instead of a sharePolicy.
63
- */
64
- keyhive?: boolean;
65
60
  /**
66
61
  * Bring your own Repo. When provided, setup skips wasm initialization and
67
62
  * repo creation entirely — you are responsible for having initialized
@@ -2,7 +2,7 @@ import type { Plugin } from "vite";
2
2
  import wasm from "vite-plugin-wasm";
3
3
  import type { PatchworkVitePluginOptions } from "./patchwork-plugin.js";
4
4
  /**
5
- * Owns envPrefix, define (__SITE_NAME__/__KEYHIVE__/__KEYHIVE_SYNC_SERVER__),
5
+ * Owns envPrefix, define (__SITE_NAME__/sync-server configuration),
6
6
  * server/preview CORS defaults, worker format + the wasm plugin, and build
7
7
  * defaults (firefox150 target, unminified, sourcemapped) — everything a site
8
8
  * used to hand-write in its own vite.config.ts. Each is switched off
@@ -1,7 +1,8 @@
1
1
  import wasm from "vite-plugin-wasm";
2
+ import { DEFAULT_SYNC_SERVERS, resolvePrimarySyncServer, } from "../site-kit/sync-servers.js";
2
3
  const CORS_HEADERS = { "Access-Control-Allow-Origin": "*" };
3
4
  /**
4
- * Owns envPrefix, define (__SITE_NAME__/__KEYHIVE__/__KEYHIVE_SYNC_SERVER__),
5
+ * Owns envPrefix, define (__SITE_NAME__/sync-server configuration),
5
6
  * server/preview CORS defaults, worker format + the wasm plugin, and build
6
7
  * defaults (firefox150 target, unminified, sourcemapped) — everything a site
7
8
  * used to hand-write in its own vite.config.ts. Each is switched off
@@ -11,9 +12,13 @@ export function configPlugin(options = {}) {
11
12
  return {
12
13
  name: "@patchwork/config",
13
14
  config() {
15
+ const primarySyncServer = resolvePrimarySyncServer(options);
16
+ const classicSyncServer = options.syncServers && typeof options.syncServers.classic === "string"
17
+ ? options.syncServers.classic
18
+ : DEFAULT_SYNC_SERVERS.classic;
14
19
  const define = {
15
- __KEYHIVE__: JSON.stringify(options.keyhive ?? false),
16
- __KEYHIVE_SYNC_SERVER__: JSON.stringify(options.keyhiveSyncServer ?? false),
20
+ __SYNC_SERVER__: JSON.stringify(primarySyncServer),
21
+ __CLASSIC_SYNC_SERVER__: JSON.stringify(classicSyncServer),
17
22
  };
18
23
  if (options.siteName) {
19
24
  define.__SITE_NAME__ = JSON.stringify(options.siteName);
@@ -31,7 +31,7 @@ export type ImportMap = {
31
31
  [scope: string]: Imports;
32
32
  };
33
33
  };
34
- export type { PatchworkSiteOptions, PatchworkIconsOptions, PatchworkHtmlOptions, PatchworkNetlifyOptions, PatchworkSyncServersOptions, } from "../site-kit/options.js";
34
+ export type { PatchworkSiteOptions, PatchworkIconsOptions, PatchworkHtmlOptions, PatchworkNetlifyOptions, PatchworkKeyhiveSyncServer, PatchworkSyncServersOptions, } from "../site-kit/options.js";
35
35
  export interface PatchworkVitePluginOptions extends PatchworkSiteOptions {
36
36
  importmap?: ImportMap;
37
37
  server?: false | ServerOptions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkandswitch/patchwork",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "author": "Ink & Switch",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,11 +37,11 @@
37
37
  "debug": "^4.4.3",
38
38
  "sharp": "^0.35.3",
39
39
  "vite-plugin-wasm": "^3.6.0",
40
- "@inkandswitch/patchwork-bootloader": "^0.4.4",
41
- "@inkandswitch/patchwork-filesystem": "^0.2.2",
42
- "@inkandswitch/patchwork-providers": "^0.4.2",
43
- "@inkandswitch/patchwork-elements": "^4.0.1",
44
- "@inkandswitch/patchwork-plugins": "^1.0.1"
40
+ "@inkandswitch/patchwork-bootloader": "^0.5.0",
41
+ "@inkandswitch/patchwork-elements": "^4.0.2",
42
+ "@inkandswitch/patchwork-filesystem": "^0.2.3",
43
+ "@inkandswitch/patchwork-plugins": "^1.0.1",
44
+ "@inkandswitch/patchwork-providers": "^0.4.2"
45
45
  },
46
46
  "devDependencies": {
47
47
  "esbuild": "^0.23.1",
package/src/client.d.ts CHANGED
@@ -13,8 +13,6 @@ import "@inkandswitch/patchwork-providers";
13
13
 
14
14
  declare global {
15
15
  const __SITE_NAME__: string;
16
- const __KEYHIVE__: boolean;
17
- const __KEYHIVE_SYNC_SERVER__: boolean;
18
16
 
19
17
  interface ImportMetaEnv {
20
18
  /**
package/src/index.ts CHANGED
@@ -152,7 +152,6 @@ async function doSetup(options: PatchworkOptions): Promise<Patchwork> {
152
152
 
153
153
  let workerAdapter = new MessageChannelNetworkAdapter(workerPort);
154
154
  ({ repo, hive, signerIdentity } = await createRepo(
155
- options,
156
155
  siteName,
157
156
  workerAdapter
158
157
  ));
package/src/repo.ts CHANGED
@@ -10,6 +10,7 @@ import {
10
10
  initKeyhiveWasm,
11
11
  initializeAutomergeRepoKeyhiveWithRepo,
12
12
  type AutomergeRepoKeyhive,
13
+ type SyncServerSelection,
13
14
  } from "@automerge/automerge-repo-keyhive";
14
15
  // eslint-disable-next-line
15
16
  // @ts-ignore — initSync is a wasm-bindgen runtime helper not in the .d.ts
@@ -21,11 +22,14 @@ import debug from "debug";
21
22
 
22
23
  const log = debug("patchwork:setup:repo");
23
24
 
24
- // Must match the automerge-worker's selection, or the tab and the SW grant
25
- // relay access to different servers.
26
- declare const __KEYHIVE_SYNC_SERVER__: boolean;
27
- const useKeyhiveSyncServer =
28
- typeof __KEYHIVE_SYNC_SERVER__ !== "undefined" && __KEYHIVE_SYNC_SERVER__;
25
+ declare const __SYNC_SERVER__: {
26
+ url: string;
27
+ keyhive?: SyncServerSelection;
28
+ };
29
+ const syncServer =
30
+ typeof __SYNC_SERVER__ !== "undefined"
31
+ ? __SYNC_SERVER__
32
+ : { url: "wss://subduction.sync.inkandswitch.com" };
29
33
 
30
34
  // Fetch and initialize automerge + subduction wasm. Memoized: the fetches start
31
35
  // on the first call and every later caller awaits the same init. Skipped
@@ -46,7 +50,6 @@ export function initWasm(): Promise<void> {
46
50
  }
47
51
 
48
52
  export async function createRepo(
49
- config: { keyhive?: boolean },
50
53
  siteName: string,
51
54
  workerAdapter: MessageChannelNetworkAdapter
52
55
  ): Promise<{
@@ -54,7 +57,7 @@ export async function createRepo(
54
57
  hive?: AutomergeRepoKeyhive;
55
58
  signerIdentity?: SignerIdentity;
56
59
  }> {
57
- if (config.keyhive) {
60
+ if (syncServer.keyhive) {
58
61
  log("setting up keyhive");
59
62
  initKeyhiveWasm();
60
63
  const { hive, repo } = await initializeAutomergeRepoKeyhiveWithRepo({
@@ -66,7 +69,7 @@ export async function createRepo(
66
69
  cachingMode: "periodic",
67
70
  onlyShareWithHardcodedServerPeerId: false,
68
71
  // ARK selects the relay via `syncServer`, defaulting to "subduction".
69
- ...(useKeyhiveSyncServer ? { syncServer: "keyhive" as const } : {}),
72
+ syncServer: syncServer.keyhive,
70
73
  repo: {
71
74
  storage: new IndexedDBWorkerStorageAdapter(),
72
75
  enableRemoteHeadsGossiping: true,
@@ -12,6 +12,7 @@ export type {
12
12
  PatchworkIconsOptions,
13
13
  PatchworkHtmlOptions,
14
14
  PatchworkNetlifyOptions,
15
+ PatchworkKeyhiveSyncServer,
15
16
  PatchworkSyncServersOptions,
16
17
  } from "./options.js";
17
18
 
@@ -1,3 +1,5 @@
1
+ import type { SyncServerIdentity } from "@automerge/automerge-repo-keyhive";
2
+
1
3
  /**
2
4
  * The bundler-agnostic option shape for a Patchwork site's generated
3
5
  * static assets (icons, index.html, manifest.webmanifest, Netlify config).
@@ -26,14 +28,19 @@ export interface PatchworkNetlifyOptions {
26
28
  immutableAssets?: boolean;
27
29
  }
28
30
 
29
- export interface PatchworkSyncServersOptions {
31
+ type PatchworkPrimarySyncServerOptions =
32
+ | { subduction?: string; keyhive?: never }
33
+ | { subduction?: never; keyhive: PatchworkKeyhiveSyncServer };
34
+
35
+ export type PatchworkKeyhiveSyncServer =
36
+ | "keyhive"
37
+ | "subduction"
38
+ | ({ url: string } & SyncServerIdentity);
39
+
40
+ export type PatchworkSyncServersOptions = {
30
41
  /** wss:// URL for the legacy automerge-repo sync-server channel (connected on demand via connectClassicSync). Default: wss://sync3.automerge.org. Pass false to skip its preconnect hint. */
31
42
  classic?: string | false;
32
- /** wss:// URL for subduction sync — live when keyhiveSyncServer is false. Default: wss://subduction.sync.inkandswitch.com */
33
- subduction?: string;
34
- /** wss:// URL for keyhive sync — live when keyhiveSyncServer is true. Default: wss://keyhive.sync.automerge.org */
35
- keyhive?: string;
36
- }
43
+ } & PatchworkPrimarySyncServerOptions;
37
44
 
38
45
  export interface PatchworkSiteOptions {
39
46
  /** -> __SITE_NAME__ define */
@@ -47,22 +54,17 @@ export interface PatchworkSiteOptions {
47
54
  /** default "/src/main.ts" — must be root-absolute, since the generated index.html doesn't live at the project root */
48
55
  entry?: string;
49
56
 
50
- /** -> __KEYHIVE__ define */
51
- keyhive?: boolean;
52
- /** -> __KEYHIVE_SYNC_SERVER__ define */
53
- keyhiveSyncServer?: boolean;
54
-
55
57
  themeColor?: string | { light: string; dark: string };
56
58
  /** manifest background_color */
57
59
  backgroundColor?: string;
58
60
 
59
61
  /**
60
- * Which sync-server hosts to preconnect/dns-prefetch from the html and
61
- * list in the Netlify Link header. Only the channel actually live for
62
- * this build is included subduction xor keyhive, picked by
63
- * `keyhiveSyncServer`, matching automerge-worker.ts's own selection
64
- * plus `classic` (on-demand, but cheap to hint) unless set to `false`.
65
- * Pass `false` to skip all sync-server hints.
62
+ * Sync-server configuration for this build. Providing `keyhive` enables
63
+ * keyhive and selects the relay identity ARK grants access to. Custom
64
+ * identities also require their WebSocket URL. `subduction` and `keyhive`
65
+ * are mutually exclusive. The live server and `classic` are also emitted as
66
+ * connection hints. Pass `false` to keep the default servers but skip those
67
+ * hints.
66
68
  */
67
69
  syncServers?: false | PatchworkSyncServersOptions;
68
70
 
@@ -1,36 +1,54 @@
1
1
  import type { PatchworkSiteOptions } from "./options.js";
2
+ import type { SyncServerSelection } from "@automerge/automerge-repo-keyhive";
2
3
 
3
4
  // Mirrors core/bootloader/src/sync-config.ts's DEFAULT_CLASSIC_SYNC_SERVER
4
5
  // and automerge-worker.ts's SUBDUCTION_SYNC_URL selection — kept here as
5
6
  // plain constants (rather than importing those runtime modules) since this
6
7
  // only needs the hostnames, not the browser-only logic that reads them.
7
- const DEFAULT_SYNC_SERVERS = {
8
+ export const DEFAULT_SYNC_SERVERS = {
8
9
  classic: "wss://sync3.automerge.org",
9
10
  subduction: "wss://subduction.sync.inkandswitch.com",
10
11
  keyhive: "wss://keyhive.sync.automerge.org",
11
12
  };
12
13
 
14
+ export function resolvePrimarySyncServer(options: PatchworkSiteOptions): {
15
+ url: string;
16
+ keyhive?: SyncServerSelection;
17
+ } {
18
+ const servers = options.syncServers || undefined;
19
+ if (servers?.keyhive) {
20
+ if (typeof servers.keyhive === "string") {
21
+ return {
22
+ keyhive: servers.keyhive,
23
+ url: DEFAULT_SYNC_SERVERS[servers.keyhive],
24
+ };
25
+ }
26
+ const { url, ...identity } = servers.keyhive;
27
+ return {
28
+ keyhive: identity,
29
+ url,
30
+ };
31
+ }
32
+ return {
33
+ url: servers?.subduction ?? DEFAULT_SYNC_SERVERS.subduction,
34
+ };
35
+ }
36
+
13
37
  function wsToHttpOrigin(wsUrl: string): string {
14
38
  return wsUrl.replace(/^ws/, "http");
15
39
  }
16
40
 
17
41
  /**
18
42
  * Resolves which sync-server origins are actually live for this build: the
19
- * channel that's live is subduction xor keyhive (picked by
20
- * `keyhiveSyncServer`, matching automerge-worker.ts's own selection) plus
21
- * classic (on-demand, but still worth a preconnect hint) unless disabled.
22
- * A flat list would drift from `keyhiveSyncServer` — e.g. always hinting
23
- * subduction even on a build that actually connects to keyhive.
43
+ * channel that's live is subduction xor keyhive plus classic (on-demand,
44
+ * but still worth a preconnect hint) unless disabled.
24
45
  */
25
46
  export function resolveSyncServers(options: PatchworkSiteOptions): string[] {
26
47
  if (options.syncServers === false) return [];
27
- const servers = { ...DEFAULT_SYNC_SERVERS, ...options.syncServers };
28
- const origins: string[] = [];
29
- const primary = options.keyhiveSyncServer
30
- ? servers.keyhive
31
- : servers.subduction;
32
- origins.push(primary);
33
- if (servers.classic) origins.push(servers.classic);
48
+ const primary = resolvePrimarySyncServer(options);
49
+ const classic = options.syncServers?.classic ?? DEFAULT_SYNC_SERVERS.classic;
50
+ const origins = [primary.url];
51
+ if (classic) origins.push(classic);
34
52
  return origins.map(wsToHttpOrigin);
35
53
  }
36
54
 
package/src/types.ts CHANGED
@@ -78,12 +78,6 @@ export interface PatchworkOptions {
78
78
  /** DOM id of the `<patchwork-view>` hosting the root tool. Defaults to "root". */
79
79
  rootElementId?: string;
80
80
 
81
- /**
82
- * Initialize keyhive for access control. The Repo then uses keyhive's network
83
- * adapter, peerId and idFactory instead of a sharePolicy.
84
- */
85
- keyhive?: boolean;
86
-
87
81
  /**
88
82
  * Bring your own Repo. When provided, setup skips wasm initialization and
89
83
  * repo creation entirely — you are responsible for having initialized
@@ -1,11 +1,15 @@
1
1
  import type { Plugin } from "vite";
2
2
  import wasm from "vite-plugin-wasm";
3
3
  import type { PatchworkVitePluginOptions } from "./patchwork-plugin.js";
4
+ import {
5
+ DEFAULT_SYNC_SERVERS,
6
+ resolvePrimarySyncServer,
7
+ } from "../site-kit/sync-servers.js";
4
8
 
5
9
  const CORS_HEADERS = { "Access-Control-Allow-Origin": "*" };
6
10
 
7
11
  /**
8
- * Owns envPrefix, define (__SITE_NAME__/__KEYHIVE__/__KEYHIVE_SYNC_SERVER__),
12
+ * Owns envPrefix, define (__SITE_NAME__/sync-server configuration),
9
13
  * server/preview CORS defaults, worker format + the wasm plugin, and build
10
14
  * defaults (firefox150 target, unminified, sourcemapped) — everything a site
11
15
  * used to hand-write in its own vite.config.ts. Each is switched off
@@ -17,11 +21,14 @@ export function configPlugin(
17
21
  return {
18
22
  name: "@patchwork/config",
19
23
  config() {
24
+ const primarySyncServer = resolvePrimarySyncServer(options);
25
+ const classicSyncServer =
26
+ options.syncServers && typeof options.syncServers.classic === "string"
27
+ ? options.syncServers.classic
28
+ : DEFAULT_SYNC_SERVERS.classic;
20
29
  const define: Record<string, string> = {
21
- __KEYHIVE__: JSON.stringify(options.keyhive ?? false),
22
- __KEYHIVE_SYNC_SERVER__: JSON.stringify(
23
- options.keyhiveSyncServer ?? false
24
- ),
30
+ __SYNC_SERVER__: JSON.stringify(primarySyncServer),
31
+ __CLASSIC_SYNC_SERVER__: JSON.stringify(classicSyncServer),
25
32
  };
26
33
  if (options.siteName) {
27
34
  define.__SITE_NAME__ = JSON.stringify(options.siteName);
@@ -54,6 +54,7 @@ export type {
54
54
  PatchworkIconsOptions,
55
55
  PatchworkHtmlOptions,
56
56
  PatchworkNetlifyOptions,
57
+ PatchworkKeyhiveSyncServer,
57
58
  PatchworkSyncServersOptions,
58
59
  } from "../site-kit/options.js";
59
60