@inkandswitch/patchwork 0.3.3 → 0.5.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 +48 -0
- package/dist/client.d.ts +2 -1
- package/dist/createAccount.d.ts +1 -1
- package/dist/createAccount.js +24 -14
- package/dist/index.js +4 -4
- package/dist/repo.d.ts +1 -1
- package/dist/repo.js +5 -4
- package/dist/router.d.ts +2 -2
- package/dist/router.js +25 -7
- package/dist/site-kit/html.d.ts +1 -1
- package/dist/site-kit/html.js +2 -1
- package/dist/site-kit/manifest.d.ts +1 -1
- package/dist/site-kit/manifest.js +2 -1
- package/dist/site-kit/options.d.ts +16 -3
- package/dist/site-kit/options.js +1 -1
- package/dist/types.d.ts +6 -5
- package/dist/vite/config-plugin.d.ts +2 -1
- package/dist/vite/config-plugin.js +8 -4
- package/package.json +3 -3
- package/src/client.d.ts +2 -1
- package/src/createAccount.ts +38 -21
- package/src/index.ts +6 -9
- package/src/repo.ts +8 -4
- package/src/router.ts +31 -10
- package/src/site-kit/html.ts +2 -2
- package/src/site-kit/manifest.ts +2 -2
- package/src/site-kit/options.ts +17 -3
- package/src/types.ts +6 -5
- package/src/vite/config-plugin.ts +10 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @inkandswitch/patchwork
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 98be594: Collapse `siteName`, `title`, and `setup({name})` into a single `title`.
|
|
8
|
+
|
|
9
|
+
A site's name was three options across two files: `siteName` and `title` in the vite config, `name` at `setup`. They fed the same handful of strings and could disagree with each other.
|
|
10
|
+
|
|
11
|
+
`title` is now the only one. It names the html `<title>`, `apple-mobile-web-app-title`, and the manifest's `name`/`short_name` as before, and is also emitted as the `__SITE_TITLE__` define, which supplies the brand word the router appends to the document title as `"<doc> | <title>"`. It defaults to `"Patchwork"`.
|
|
12
|
+
|
|
13
|
+
- `siteName` and the `__SITE_NAME__` define are removed. If you used `siteName` only for display, rename it to `title`; if you relied on it to namespace storage, see `storagePrefix`.
|
|
14
|
+
- `setup({name})` is now `setup({title})`, and is only needed to override the build-time value.
|
|
15
|
+
|
|
16
|
+
- 98be594: Namespace IndexedDB and peer ids with a new build-time `storagePrefix` option.
|
|
17
|
+
|
|
18
|
+
The tab and the shared automerge worker are separate bundles that must open the same databases. Both now read the name from one place, `@inkandswitch/patchwork-bootloader/storage`, resolved from the `__STORAGE_PREFIX__` define the vite plugin emits unconditionally.
|
|
19
|
+
|
|
20
|
+
Previously each side resolved `__SITE_NAME__` itself with a different fallback — `"patchwork.inkandswitch.com"` in the worker, `"patchwork"` in the tab — so a site that never set `siteName` had its tab and worker on two different keyhive databases, and one that passed `setup({name})` split them the same way, since a runtime option never reaches the worker.
|
|
21
|
+
|
|
22
|
+
- `storagePrefix` defaults to `"patchwork"` and is settable only in the build config. Sites sharing an origin must use distinct prefixes. It is deliberately not derived from any display name: changing it points a site at empty storage, so a rebrand must not be able to change it by accident.
|
|
23
|
+
- Sites that relied on `siteName` to namespace their storage must now set `storagePrefix` explicitly to that same value to keep their existing databases.
|
|
24
|
+
- `createRepo` in `@inkandswitch/patchwork` no longer takes a site name argument.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- Updated dependencies [98be594]
|
|
29
|
+
- @inkandswitch/patchwork-bootloader@0.6.0
|
|
30
|
+
|
|
31
|
+
## 0.4.0
|
|
32
|
+
|
|
33
|
+
### Minor Changes
|
|
34
|
+
|
|
35
|
+
- 776b9bb: Boot no longer waits on datatypes it doesn't own. `resolveAccountHandle` writes
|
|
36
|
+
the account doc directly instead of blocking on `loadWhenReady("account")`, and
|
|
37
|
+
`createDefaultAccount` builds the root folder, module-settings and anonymous
|
|
38
|
+
contact subdocs itself rather than waiting for the `folder`,
|
|
39
|
+
`patchwork:module-settings` and `contact` datatypes. A package bundle that never
|
|
40
|
+
registers those no longer hangs setup until its timeout.
|
|
41
|
+
|
|
42
|
+
`AccountDoc.frameToolId` is now optional. `createDefaultAccount` still writes
|
|
43
|
+
`"threepane"`, and the router falls back to the first registered `frame-tool`
|
|
44
|
+
when an account has none.
|
|
45
|
+
|
|
46
|
+
### Patch Changes
|
|
47
|
+
|
|
48
|
+
- Updated dependencies [776b9bb]
|
|
49
|
+
- @inkandswitch/patchwork-plugins@1.2.0
|
|
50
|
+
|
|
3
51
|
## 0.3.3
|
|
4
52
|
|
|
5
53
|
### Patch Changes
|
package/dist/client.d.ts
CHANGED
package/dist/createAccount.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { AccountCreator, AccountDoc } from "@inkandswitch/patchwork-plugins";
|
|
2
2
|
export declare const createDefaultAccount: AccountCreator<AccountDoc>;
|
package/dist/createAccount.js
CHANGED
|
@@ -1,18 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
async function createSubdoc(repo, type, init) {
|
|
2
|
+
const handle = await repo.create2();
|
|
3
|
+
handle.change((doc) => {
|
|
4
|
+
doc["@patchwork"] = { type };
|
|
5
|
+
init(doc);
|
|
6
|
+
});
|
|
7
|
+
return handle;
|
|
8
|
+
}
|
|
2
9
|
export const createDefaultAccount = async (accountHandle, repo) => {
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
const [rootFolder, moduleSettings, contact] = await Promise.all([
|
|
11
|
+
createSubdoc(repo, "folder", (doc) => {
|
|
12
|
+
doc.title = "New Folder";
|
|
13
|
+
doc.docs = [];
|
|
14
|
+
}),
|
|
15
|
+
createSubdoc(repo, "patchwork:module-settings", (doc) => {
|
|
16
|
+
doc.modules = [];
|
|
17
|
+
}),
|
|
18
|
+
createSubdoc(repo, "contact", (doc) => {
|
|
19
|
+
doc.type = "anonymous";
|
|
20
|
+
}),
|
|
21
|
+
]);
|
|
14
22
|
accountHandle.change((doc) => {
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
doc.frameToolId = "threepane";
|
|
24
|
+
doc.rootFolderUrl = rootFolder.url;
|
|
25
|
+
doc.moduleSettingsUrl = moduleSettings.url;
|
|
26
|
+
doc.contactUrl = contact.url;
|
|
17
27
|
});
|
|
18
28
|
};
|
package/dist/index.js
CHANGED
|
@@ -53,8 +53,8 @@ export function setup(options = {}) {
|
|
|
53
53
|
}
|
|
54
54
|
export default setup;
|
|
55
55
|
async function doSetup(options) {
|
|
56
|
-
const
|
|
57
|
-
(typeof
|
|
56
|
+
const siteTitle = options.title ??
|
|
57
|
+
(typeof __SITE_TITLE__ !== "undefined" ? __SITE_TITLE__ : "Patchwork");
|
|
58
58
|
const moduleSources = resolveDefaultModules(options);
|
|
59
59
|
const routing = options.routing ?? "hash";
|
|
60
60
|
log("booting", options);
|
|
@@ -85,7 +85,7 @@ async function doSetup(options) {
|
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
87
|
let workerAdapter = new MessageChannelNetworkAdapter(workerPort);
|
|
88
|
-
({ repo, hive, signerIdentity } = await createRepo(
|
|
88
|
+
({ repo, hive, signerIdentity } = await createRepo(workerAdapter));
|
|
89
89
|
// The worker was recreated with cold state: wire the repo onto the fresh
|
|
90
90
|
// port and drop the adapter stranded on the dead one.
|
|
91
91
|
const bootHive = hive;
|
|
@@ -154,7 +154,7 @@ async function doSetup(options) {
|
|
|
154
154
|
rootElement,
|
|
155
155
|
repo,
|
|
156
156
|
accountDocHandle,
|
|
157
|
-
|
|
157
|
+
siteTitle,
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
160
|
installReveal(rootElement, router, toolsLoaded);
|
package/dist/repo.d.ts
CHANGED
|
@@ -3,7 +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(
|
|
6
|
+
export declare function createRepo(workerAdapter: MessageChannelNetworkAdapter): Promise<{
|
|
7
7
|
repo: Repo;
|
|
8
8
|
hive?: AutomergeRepoKeyhive;
|
|
9
9
|
signerIdentity?: SignerIdentity;
|
package/dist/repo.js
CHANGED
|
@@ -5,6 +5,7 @@ import { initKeyhiveWasm, initializeAutomergeRepoKeyhiveWithRepo, } from "@autom
|
|
|
5
5
|
// @ts-ignore — initSync is a wasm-bindgen runtime helper not in the .d.ts
|
|
6
6
|
import { initSync as initSubductionSync } from "@automerge/automerge-subduction/slim";
|
|
7
7
|
import { MemorySigner } from "@automerge/automerge-subduction/slim";
|
|
8
|
+
import { keyhiveStorageName, storagePrefix, } from "@inkandswitch/patchwork-bootloader/storage";
|
|
8
9
|
import debug from "debug";
|
|
9
10
|
const log = debug("patchwork:setup:repo");
|
|
10
11
|
const syncServer = typeof __SYNC_SERVER__ !== "undefined"
|
|
@@ -27,14 +28,14 @@ export function initWasm() {
|
|
|
27
28
|
}
|
|
28
29
|
return wasmReady;
|
|
29
30
|
}
|
|
30
|
-
export async function createRepo(
|
|
31
|
+
export async function createRepo(workerAdapter) {
|
|
31
32
|
if (syncServer.keyhive) {
|
|
32
33
|
log("setting up keyhive");
|
|
33
34
|
initKeyhiveWasm();
|
|
34
35
|
const { hive, repo } = await initializeAutomergeRepoKeyhiveWithRepo({
|
|
35
36
|
createRepo: (repoConfig) => new Repo(repoConfig),
|
|
36
|
-
storage: new IndexedDBWorkerStorageAdapter(
|
|
37
|
-
peerIdSuffix:
|
|
37
|
+
storage: new IndexedDBWorkerStorageAdapter(keyhiveStorageName),
|
|
38
|
+
peerIdSuffix: storagePrefix + Math.random().toString(36).slice(2),
|
|
38
39
|
networkAdapter: workerAdapter,
|
|
39
40
|
automaticArchiveIngestion: true,
|
|
40
41
|
cachingMode: "periodic",
|
|
@@ -61,7 +62,7 @@ export async function createRepo(siteName, workerAdapter) {
|
|
|
61
62
|
return peerId.includes("automerge-worker");
|
|
62
63
|
},
|
|
63
64
|
enableRemoteHeadsGossiping: true,
|
|
64
|
-
peerId: `${
|
|
65
|
+
peerId: `${storagePrefix}-tab-${crypto.randomUUID()}`,
|
|
65
66
|
});
|
|
66
67
|
const signerIdentity = {
|
|
67
68
|
peerId: signer.peerId().toString(),
|
package/dist/router.d.ts
CHANGED
|
@@ -9,10 +9,10 @@ export interface RouterParams {
|
|
|
9
9
|
rootElement: HTMLElement;
|
|
10
10
|
repo: Repo;
|
|
11
11
|
accountDocHandle: DocHandle<AccountDoc>;
|
|
12
|
-
|
|
12
|
+
siteTitle: string;
|
|
13
13
|
}
|
|
14
14
|
export interface Router {
|
|
15
15
|
/** Apply `location.hash` to the view. */
|
|
16
16
|
route(): Promise<void>;
|
|
17
17
|
}
|
|
18
|
-
export declare function createRouter({ rootElement, repo, accountDocHandle,
|
|
18
|
+
export declare function createRouter({ rootElement, repo, accountDocHandle, siteTitle, }: RouterParams): Router;
|
package/dist/router.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isValidAutomergeUrl, isValidDocumentId, stringifyAutomergeUrl, } from "@automerge/vanillajs/slim";
|
|
1
|
+
import { isValidAutomergeUrl, isValidDocumentId, parseAutomergeUrl, stringifyAutomergeUrl, } from "@automerge/vanillajs/slim";
|
|
2
2
|
import { openDocument } from "@inkandswitch/patchwork-elements";
|
|
3
3
|
import { getRegistry, } from "@inkandswitch/patchwork-plugins";
|
|
4
4
|
// Legacy big-patchwork hash shape: `<slug>--<documentId>[?…]`. The slug can
|
|
@@ -6,12 +6,12 @@ import { getRegistry, } from "@inkandswitch/patchwork-plugins";
|
|
|
6
6
|
// anchor on the `--` before the base58 document id rather than a strict slug
|
|
7
7
|
// charset.
|
|
8
8
|
const BIG_PATCHWORK_HASH_REGEX = /^(?<title>[^=&?/#]*)--(?<docId>[1-9A-HJ-NP-Za-km-z]+)/;
|
|
9
|
-
// The `doc=`
|
|
9
|
+
// The `doc=` and `draft=` values are automerge URLs, kept literal rather than
|
|
10
10
|
// percent-encoded so links stay readable.
|
|
11
|
-
const RAW_HASH_KEYS = new Set(["doc"]);
|
|
11
|
+
const RAW_HASH_KEYS = new Set(["doc", "draft"]);
|
|
12
12
|
// A stable order means re-serializing the same logical params is
|
|
13
13
|
// byte-identical, avoiding spurious `hashchange` round-trips.
|
|
14
|
-
const HASH_KEY_ORDER = ["doc", "tool", "type", "title", "frame"];
|
|
14
|
+
const HASH_KEY_ORDER = ["doc", "tool", "type", "title", "frame", "draft"];
|
|
15
15
|
function serializeHashParams(params) {
|
|
16
16
|
const keys = [...HASH_KEY_ORDER, ...params.keys()];
|
|
17
17
|
const parts = [];
|
|
@@ -42,14 +42,24 @@ export function docParamToUrl(docParam) {
|
|
|
42
42
|
return undefined;
|
|
43
43
|
return stringifyAutomergeUrl({ documentId: documentId });
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
function registeredFrameToolId() {
|
|
46
|
+
return getRegistry("patchwork:tool")
|
|
47
|
+
.filter((tool) => !!tool.tags?.includes("frame-tool") && !tool.unlisted)
|
|
48
|
+
.at(0)?.id;
|
|
49
|
+
}
|
|
50
|
+
export function createRouter({ rootElement, repo, accountDocHandle, siteTitle, }) {
|
|
46
51
|
const route = async () => {
|
|
47
52
|
// The first call seeds the root view's tool/doc so it can mount; later
|
|
48
53
|
// calls reconcile the mounted view with the hash.
|
|
49
54
|
if (!rootElement.hasAttribute("tool-id")) {
|
|
50
55
|
const params = new URLSearchParams(location.hash.slice(1));
|
|
51
56
|
const frame = params.get("frame");
|
|
52
|
-
|
|
57
|
+
const toolId = frame ?? accountDocHandle.doc().frameToolId ?? registeredFrameToolId();
|
|
58
|
+
if (!toolId) {
|
|
59
|
+
console.error("patchwork: no frame tool registered, nothing to mount");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
rootElement.setAttribute("tool-id", toolId);
|
|
53
63
|
rootElement.setAttribute("doc-url", (frame && docParamToUrl(params.get("doc"))) || accountDocHandle.url);
|
|
54
64
|
return;
|
|
55
65
|
}
|
|
@@ -97,6 +107,14 @@ export function createRouter({ rootElement, repo, accountDocHandle, siteName, })
|
|
|
97
107
|
// `doc` is the full automerge URL, so heads live inside it and the separate
|
|
98
108
|
// `heads=` param is gone.
|
|
99
109
|
params.delete("heads");
|
|
110
|
+
// `draft` is doc-scoped (owned by the drafts plugin, opaque here):
|
|
111
|
+
// navigating to a different document invalidates the selection, while
|
|
112
|
+
// same-doc navigation (tool switches, heads changes) keeps it.
|
|
113
|
+
const prevDoc = docParamToUrl(params.get("doc"));
|
|
114
|
+
if (prevDoc &&
|
|
115
|
+
parseAutomergeUrl(prevDoc).documentId !== parseAutomergeUrl(url).documentId) {
|
|
116
|
+
params.delete("draft");
|
|
117
|
+
}
|
|
100
118
|
params.set("doc", url);
|
|
101
119
|
for (const [key, value] of [
|
|
102
120
|
["tool", toolId],
|
|
@@ -120,7 +138,7 @@ export function createRouter({ rootElement, repo, accountDocHandle, siteName, })
|
|
|
120
138
|
return;
|
|
121
139
|
const docTitle = datatype.module.getTitle(doc);
|
|
122
140
|
if (docTitle)
|
|
123
|
-
document.title = `${docTitle} | ${
|
|
141
|
+
document.title = `${docTitle} | ${siteTitle}`;
|
|
124
142
|
}
|
|
125
143
|
catch (e) {
|
|
126
144
|
console.error("Failed to update document title", e);
|
package/dist/site-kit/html.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PatchworkSiteOptions } from "./options.js";
|
|
2
2
|
export declare function escapeHtml(value: string): string;
|
|
3
3
|
/** Builds the generated index.html as a plain string — no bundler involved. */
|
|
4
4
|
export declare function buildHtml(options: PatchworkSiteOptions): string;
|
package/dist/site-kit/html.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DEFAULT_TITLE } from "./options.js";
|
|
1
2
|
import { resolveSyncServers, PRELOAD_WASM_ASSETS } from "./sync-servers.js";
|
|
2
3
|
import { ICON_SPECS } from "./icons.js";
|
|
3
4
|
const HTML_ESCAPES = {
|
|
@@ -12,7 +13,7 @@ export function escapeHtml(value) {
|
|
|
12
13
|
}
|
|
13
14
|
/** Builds the generated index.html as a plain string — no bundler involved. */
|
|
14
15
|
export function buildHtml(options) {
|
|
15
|
-
const title = options.title ??
|
|
16
|
+
const title = options.title ?? DEFAULT_TITLE;
|
|
16
17
|
const lang = (options.html && options.html.lang) || "en";
|
|
17
18
|
const entry = options.entry ?? "/src/main.ts";
|
|
18
19
|
const syncServers = resolveSyncServers(options);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PatchworkSiteOptions } from "./options.js";
|
|
2
2
|
/** Builds the generated manifest.webmanifest object — no bundler involved. */
|
|
3
3
|
export declare function buildManifest(options: PatchworkSiteOptions): Record<string, unknown>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { DEFAULT_TITLE } from "./options.js";
|
|
1
2
|
import { ICON_SPECS } from "./icons.js";
|
|
2
3
|
/** Builds the generated manifest.webmanifest object — no bundler involved. */
|
|
3
4
|
export function buildManifest(options) {
|
|
4
|
-
const title = options.title ??
|
|
5
|
+
const title = options.title ?? DEFAULT_TITLE;
|
|
5
6
|
const icons = !options.icons
|
|
6
7
|
? []
|
|
7
8
|
: ICON_SPECS.filter((spec) => spec.fileName === "apple-touch-icon.png" || spec.manifestPurpose).map((spec) => ({
|
|
@@ -37,10 +37,23 @@ export type PatchworkSyncServersOptions = {
|
|
|
37
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. */
|
|
38
38
|
classic?: string | false;
|
|
39
39
|
} & PatchworkPrimarySyncServerOptions;
|
|
40
|
+
export declare const DEFAULT_TITLE = "Patchwork";
|
|
40
41
|
export interface PatchworkSiteOptions {
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Namespace for this site's IndexedDB databases and peer ids
|
|
44
|
+
* (-> __STORAGE_PREFIX__ define). Defaults to `"patchwork"`. Sites sharing
|
|
45
|
+
* an origin MUST use distinct prefixes.
|
|
46
|
+
*
|
|
47
|
+
* The tab and the shared automerge worker are separate bundles that have to
|
|
48
|
+
* open the same databases, so this is settable only here, where both of them
|
|
49
|
+
* receive it. Changing it on an existing site points it at empty storage.
|
|
50
|
+
*/
|
|
51
|
+
storagePrefix?: string;
|
|
52
|
+
/**
|
|
53
|
+
* This site's name: `<title>`, apple-mobile-web-app-title, manifest name,
|
|
54
|
+
* and — via the __SITE_TITLE__ define — the brand word the router appends to
|
|
55
|
+
* the document title as `"<doc> | <title>"`. Defaults to `"Patchwork"`.
|
|
56
|
+
*/
|
|
44
57
|
title?: string;
|
|
45
58
|
/** manifest short_name (defaults to title) */
|
|
46
59
|
shortName?: string;
|
package/dist/site-kit/options.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export const DEFAULT_TITLE = "Patchwork";
|
package/dist/types.d.ts
CHANGED
|
@@ -47,13 +47,14 @@ export interface PatchworkOptions {
|
|
|
47
47
|
accountKey?: string;
|
|
48
48
|
createAccount?: AccountCreator;
|
|
49
49
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* for you), and used to namespace this site's storage and peer ids.
|
|
50
|
+
* This site's name, appended to the document title as `"<doc> | <title>"`
|
|
51
|
+
* when a document is open (the separator is provided for you).
|
|
53
52
|
*
|
|
54
|
-
* Defaults to the build-time `
|
|
53
|
+
* Defaults to the build-time `__SITE_TITLE__` define — the vite plugin's
|
|
54
|
+
* `title` option, which also names the html `<title>` and the manifest —
|
|
55
|
+
* then `"Patchwork"`.
|
|
55
56
|
*/
|
|
56
|
-
|
|
57
|
+
title?: string;
|
|
57
58
|
/** DOM id of the `<patchwork-view>` hosting the root tool. Defaults to "root". */
|
|
58
59
|
rootElementId?: string;
|
|
59
60
|
/**
|
|
@@ -2,7 +2,8 @@ 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 (
|
|
5
|
+
* Owns envPrefix, define (__SITE_TITLE__/__STORAGE_PREFIX__/sync-server
|
|
6
|
+
* configuration),
|
|
6
7
|
* server/preview CORS defaults, worker format + the wasm plugin, and build
|
|
7
8
|
* defaults (firefox150 target, unminified, sourcemapped) — everything a site
|
|
8
9
|
* used to hand-write in its own vite.config.ts. Each is switched off
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import wasm from "vite-plugin-wasm";
|
|
2
|
+
import { DEFAULT_STORAGE_PREFIX } from "@inkandswitch/patchwork-bootloader/storage";
|
|
3
|
+
import { DEFAULT_TITLE } from "../site-kit/options.js";
|
|
2
4
|
import { DEFAULT_SYNC_SERVERS, resolvePrimarySyncServer, } from "../site-kit/sync-servers.js";
|
|
3
5
|
const CORS_HEADERS = { "Access-Control-Allow-Origin": "*" };
|
|
4
6
|
/**
|
|
5
|
-
* Owns envPrefix, define (
|
|
7
|
+
* Owns envPrefix, define (__SITE_TITLE__/__STORAGE_PREFIX__/sync-server
|
|
8
|
+
* configuration),
|
|
6
9
|
* server/preview CORS defaults, worker format + the wasm plugin, and build
|
|
7
10
|
* defaults (firefox150 target, unminified, sourcemapped) — everything a site
|
|
8
11
|
* used to hand-write in its own vite.config.ts. Each is switched off
|
|
@@ -16,13 +19,14 @@ export function configPlugin(options = {}) {
|
|
|
16
19
|
const classicSyncServer = options.syncServers && typeof options.syncServers.classic === "string"
|
|
17
20
|
? options.syncServers.classic
|
|
18
21
|
: DEFAULT_SYNC_SERVERS.classic;
|
|
22
|
+
// __STORAGE_PREFIX__ is defined unconditionally: the tab and the shared
|
|
23
|
+
// automerge worker resolve it separately, and only a define reaches both.
|
|
19
24
|
const define = {
|
|
20
25
|
__SYNC_SERVER__: JSON.stringify(primarySyncServer),
|
|
21
26
|
__CLASSIC_SYNC_SERVER__: JSON.stringify(classicSyncServer),
|
|
27
|
+
__SITE_TITLE__: JSON.stringify(options.title ?? DEFAULT_TITLE),
|
|
28
|
+
__STORAGE_PREFIX__: JSON.stringify(options.storagePrefix ?? DEFAULT_STORAGE_PREFIX),
|
|
22
29
|
};
|
|
23
|
-
if (options.siteName) {
|
|
24
|
-
define.__SITE_NAME__ = JSON.stringify(options.siteName);
|
|
25
|
-
}
|
|
26
30
|
return {
|
|
27
31
|
envPrefix: ["VITE_", "PATCHWORK_"],
|
|
28
32
|
define,
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"url": "git+https://github.com/inkandswitch/patchwork-system.git",
|
|
6
6
|
"directory": "core/patchwork"
|
|
7
7
|
},
|
|
8
|
-
"version": "0.
|
|
8
|
+
"version": "0.5.0",
|
|
9
9
|
"author": "Ink & Switch",
|
|
10
10
|
"type": "module",
|
|
11
11
|
"license": "MIT",
|
|
@@ -42,10 +42,10 @@
|
|
|
42
42
|
"debug": "^4.4.3",
|
|
43
43
|
"sharp": "^0.35.3",
|
|
44
44
|
"vite-plugin-wasm": "^3.6.0",
|
|
45
|
-
"@inkandswitch/patchwork-bootloader": "^0.5.4",
|
|
46
45
|
"@inkandswitch/patchwork-elements": "^6.0.0",
|
|
46
|
+
"@inkandswitch/patchwork-plugins": "^1.2.0",
|
|
47
|
+
"@inkandswitch/patchwork-bootloader": "^0.6.0",
|
|
47
48
|
"@inkandswitch/patchwork-filesystem": "^0.2.5",
|
|
48
|
-
"@inkandswitch/patchwork-plugins": "^1.1.0",
|
|
49
49
|
"@inkandswitch/patchwork-providers": "^0.5.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
package/src/client.d.ts
CHANGED
package/src/createAccount.ts
CHANGED
|
@@ -1,31 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
createDocOfDatatype2,
|
|
6
|
-
getRegistry,
|
|
1
|
+
import type { Repo } from "@automerge/automerge-repo/slim";
|
|
2
|
+
import type {
|
|
3
|
+
AccountCreator,
|
|
4
|
+
AccountDoc,
|
|
7
5
|
} from "@inkandswitch/patchwork-plugins";
|
|
6
|
+
import type { HasPatchworkMetadata } from "@inkandswitch/patchwork-filesystem";
|
|
7
|
+
|
|
8
|
+
async function createSubdoc<D>(
|
|
9
|
+
repo: Repo,
|
|
10
|
+
type: string,
|
|
11
|
+
init: (doc: D) => void
|
|
12
|
+
) {
|
|
13
|
+
const handle = await repo.create2<D & HasPatchworkMetadata>();
|
|
14
|
+
handle.change((doc: D & HasPatchworkMetadata) => {
|
|
15
|
+
doc["@patchwork"] = { type };
|
|
16
|
+
init(doc);
|
|
17
|
+
});
|
|
18
|
+
return handle;
|
|
19
|
+
}
|
|
8
20
|
|
|
9
21
|
export const createDefaultAccount: AccountCreator<AccountDoc> = async (
|
|
10
22
|
accountHandle,
|
|
11
23
|
repo
|
|
12
24
|
) => {
|
|
13
|
-
const
|
|
14
|
-
[
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
})
|
|
26
|
-
|
|
25
|
+
const [rootFolder, moduleSettings, contact] = await Promise.all([
|
|
26
|
+
createSubdoc<{ title: string; docs: unknown[] }>(repo, "folder", (doc) => {
|
|
27
|
+
doc.title = "New Folder";
|
|
28
|
+
doc.docs = [];
|
|
29
|
+
}),
|
|
30
|
+
createSubdoc<{ modules: string[] }>(
|
|
31
|
+
repo,
|
|
32
|
+
"patchwork:module-settings",
|
|
33
|
+
(doc) => {
|
|
34
|
+
doc.modules = [];
|
|
35
|
+
}
|
|
36
|
+
),
|
|
37
|
+
createSubdoc<{ type: string }>(repo, "contact", (doc) => {
|
|
38
|
+
doc.type = "anonymous";
|
|
39
|
+
}),
|
|
40
|
+
]);
|
|
27
41
|
|
|
28
42
|
accountHandle.change((doc) => {
|
|
29
|
-
|
|
43
|
+
doc.frameToolId = "threepane";
|
|
44
|
+
doc.rootFolderUrl = rootFolder.url;
|
|
45
|
+
doc.moduleSettingsUrl = moduleSettings.url;
|
|
46
|
+
doc.contactUrl = contact.url;
|
|
30
47
|
});
|
|
31
48
|
};
|
package/src/index.ts
CHANGED
|
@@ -64,7 +64,7 @@ import { createDefaultAccount } from "./createAccount.js";
|
|
|
64
64
|
|
|
65
65
|
const log = debug("patchwork:setup");
|
|
66
66
|
|
|
67
|
-
declare const
|
|
67
|
+
declare const __SITE_TITLE__: string;
|
|
68
68
|
|
|
69
69
|
declare global {
|
|
70
70
|
interface Window {
|
|
@@ -114,9 +114,9 @@ export function setup(options: PatchworkOptions = {}): Promise<Patchwork> {
|
|
|
114
114
|
export default setup;
|
|
115
115
|
|
|
116
116
|
async function doSetup(options: PatchworkOptions): Promise<Patchwork> {
|
|
117
|
-
const
|
|
118
|
-
options.
|
|
119
|
-
(typeof
|
|
117
|
+
const siteTitle =
|
|
118
|
+
options.title ??
|
|
119
|
+
(typeof __SITE_TITLE__ !== "undefined" ? __SITE_TITLE__ : "Patchwork");
|
|
120
120
|
const moduleSources = resolveDefaultModules(options);
|
|
121
121
|
const routing = options.routing ?? "hash";
|
|
122
122
|
|
|
@@ -151,10 +151,7 @@ async function doSetup(options: PatchworkOptions): Promise<Patchwork> {
|
|
|
151
151
|
});
|
|
152
152
|
|
|
153
153
|
let workerAdapter = new MessageChannelNetworkAdapter(workerPort);
|
|
154
|
-
({ repo, hive, signerIdentity } = await createRepo(
|
|
155
|
-
siteName,
|
|
156
|
-
workerAdapter
|
|
157
|
-
));
|
|
154
|
+
({ repo, hive, signerIdentity } = await createRepo(workerAdapter));
|
|
158
155
|
|
|
159
156
|
// The worker was recreated with cold state: wire the repo onto the fresh
|
|
160
157
|
// port and drop the adapter stranded on the dead one.
|
|
@@ -246,7 +243,7 @@ async function doSetup(options: PatchworkOptions): Promise<Patchwork> {
|
|
|
246
243
|
rootElement,
|
|
247
244
|
repo,
|
|
248
245
|
accountDocHandle,
|
|
249
|
-
|
|
246
|
+
siteTitle,
|
|
250
247
|
});
|
|
251
248
|
}
|
|
252
249
|
|
package/src/repo.ts
CHANGED
|
@@ -17,6 +17,10 @@ import {
|
|
|
17
17
|
import { initSync as initSubductionSync } from "@automerge/automerge-subduction/slim";
|
|
18
18
|
import { MemorySigner } from "@automerge/automerge-subduction/slim";
|
|
19
19
|
import setupServiceWorker from "@inkandswitch/patchwork-bootloader";
|
|
20
|
+
import {
|
|
21
|
+
keyhiveStorageName,
|
|
22
|
+
storagePrefix,
|
|
23
|
+
} from "@inkandswitch/patchwork-bootloader/storage";
|
|
20
24
|
import type { SignerIdentity } from "./types.js";
|
|
21
25
|
import debug from "debug";
|
|
22
26
|
|
|
@@ -50,7 +54,6 @@ export function initWasm(): Promise<void> {
|
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
export async function createRepo(
|
|
53
|
-
siteName: string,
|
|
54
57
|
workerAdapter: MessageChannelNetworkAdapter
|
|
55
58
|
): Promise<{
|
|
56
59
|
repo: Repo;
|
|
@@ -62,8 +65,8 @@ export async function createRepo(
|
|
|
62
65
|
initKeyhiveWasm();
|
|
63
66
|
const { hive, repo } = await initializeAutomergeRepoKeyhiveWithRepo({
|
|
64
67
|
createRepo: (repoConfig) => new Repo(repoConfig),
|
|
65
|
-
storage: new IndexedDBWorkerStorageAdapter(
|
|
66
|
-
peerIdSuffix:
|
|
68
|
+
storage: new IndexedDBWorkerStorageAdapter(keyhiveStorageName),
|
|
69
|
+
peerIdSuffix: storagePrefix + Math.random().toString(36).slice(2),
|
|
67
70
|
networkAdapter: workerAdapter,
|
|
68
71
|
automaticArchiveIngestion: true,
|
|
69
72
|
cachingMode: "periodic",
|
|
@@ -91,7 +94,8 @@ export async function createRepo(
|
|
|
91
94
|
return peerId.includes("automerge-worker");
|
|
92
95
|
},
|
|
93
96
|
enableRemoteHeadsGossiping: true,
|
|
94
|
-
peerId:
|
|
97
|
+
peerId:
|
|
98
|
+
`${storagePrefix}-tab-${crypto.randomUUID()}` as AutomergeRepo.PeerId,
|
|
95
99
|
});
|
|
96
100
|
const signerIdentity = {
|
|
97
101
|
peerId: signer.peerId().toString(),
|
package/src/router.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isValidAutomergeUrl,
|
|
3
3
|
isValidDocumentId,
|
|
4
|
+
parseAutomergeUrl,
|
|
4
5
|
stringifyAutomergeUrl,
|
|
5
6
|
type AutomergeUrl,
|
|
6
7
|
type DocHandle,
|
|
@@ -12,6 +13,7 @@ import {
|
|
|
12
13
|
type AccountDoc,
|
|
13
14
|
type DatatypeDescription,
|
|
14
15
|
type DatatypeImplementation,
|
|
16
|
+
type ToolDescription,
|
|
15
17
|
getRegistry,
|
|
16
18
|
} from "@inkandswitch/patchwork-plugins";
|
|
17
19
|
|
|
@@ -22,12 +24,12 @@ import {
|
|
|
22
24
|
const BIG_PATCHWORK_HASH_REGEX =
|
|
23
25
|
/^(?<title>[^=&?/#]*)--(?<docId>[1-9A-HJ-NP-Za-km-z]+)/;
|
|
24
26
|
|
|
25
|
-
// The `doc=`
|
|
27
|
+
// The `doc=` and `draft=` values are automerge URLs, kept literal rather than
|
|
26
28
|
// percent-encoded so links stay readable.
|
|
27
|
-
const RAW_HASH_KEYS = new Set(["doc"]);
|
|
29
|
+
const RAW_HASH_KEYS = new Set(["doc", "draft"]);
|
|
28
30
|
// A stable order means re-serializing the same logical params is
|
|
29
31
|
// byte-identical, avoiding spurious `hashchange` round-trips.
|
|
30
|
-
const HASH_KEY_ORDER = ["doc", "tool", "type", "title", "frame"];
|
|
32
|
+
const HASH_KEY_ORDER = ["doc", "tool", "type", "title", "frame", "draft"];
|
|
31
33
|
|
|
32
34
|
function serializeHashParams(params: URLSearchParams): string {
|
|
33
35
|
const keys = [...HASH_KEY_ORDER, ...params.keys()];
|
|
@@ -63,7 +65,7 @@ export interface RouterParams {
|
|
|
63
65
|
rootElement: HTMLElement;
|
|
64
66
|
repo: Repo;
|
|
65
67
|
accountDocHandle: DocHandle<AccountDoc>;
|
|
66
|
-
|
|
68
|
+
siteTitle: string;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
export interface Router {
|
|
@@ -71,11 +73,17 @@ export interface Router {
|
|
|
71
73
|
route(): Promise<void>;
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
function registeredFrameToolId(): string | undefined {
|
|
77
|
+
return getRegistry<ToolDescription>("patchwork:tool")
|
|
78
|
+
.filter((tool) => !!tool.tags?.includes("frame-tool") && !tool.unlisted)
|
|
79
|
+
.at(0)?.id;
|
|
80
|
+
}
|
|
81
|
+
|
|
74
82
|
export function createRouter({
|
|
75
83
|
rootElement,
|
|
76
84
|
repo,
|
|
77
85
|
accountDocHandle,
|
|
78
|
-
|
|
86
|
+
siteTitle,
|
|
79
87
|
}: RouterParams): Router {
|
|
80
88
|
const route = async () => {
|
|
81
89
|
// The first call seeds the root view's tool/doc so it can mount; later
|
|
@@ -83,10 +91,13 @@ export function createRouter({
|
|
|
83
91
|
if (!rootElement.hasAttribute("tool-id")) {
|
|
84
92
|
const params = new URLSearchParams(location.hash.slice(1));
|
|
85
93
|
const frame = params.get("frame");
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
const toolId =
|
|
95
|
+
frame ?? accountDocHandle.doc().frameToolId ?? registeredFrameToolId();
|
|
96
|
+
if (!toolId) {
|
|
97
|
+
console.error("patchwork: no frame tool registered, nothing to mount");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
rootElement.setAttribute("tool-id", toolId);
|
|
90
101
|
rootElement.setAttribute(
|
|
91
102
|
"doc-url",
|
|
92
103
|
(frame && docParamToUrl(params.get("doc"))) || accountDocHandle.url
|
|
@@ -156,6 +167,16 @@ export function createRouter({
|
|
|
156
167
|
// `doc` is the full automerge URL, so heads live inside it and the separate
|
|
157
168
|
// `heads=` param is gone.
|
|
158
169
|
params.delete("heads");
|
|
170
|
+
// `draft` is doc-scoped (owned by the drafts plugin, opaque here):
|
|
171
|
+
// navigating to a different document invalidates the selection, while
|
|
172
|
+
// same-doc navigation (tool switches, heads changes) keeps it.
|
|
173
|
+
const prevDoc = docParamToUrl(params.get("doc"));
|
|
174
|
+
if (
|
|
175
|
+
prevDoc &&
|
|
176
|
+
parseAutomergeUrl(prevDoc).documentId !== parseAutomergeUrl(url).documentId
|
|
177
|
+
) {
|
|
178
|
+
params.delete("draft");
|
|
179
|
+
}
|
|
159
180
|
params.set("doc", url);
|
|
160
181
|
for (const [key, value] of [
|
|
161
182
|
["tool", toolId],
|
|
@@ -182,7 +203,7 @@ export function createRouter({
|
|
|
182
203
|
const docTitle = (datatype.module as DatatypeImplementation).getTitle(
|
|
183
204
|
doc
|
|
184
205
|
);
|
|
185
|
-
if (docTitle) document.title = `${docTitle} | ${
|
|
206
|
+
if (docTitle) document.title = `${docTitle} | ${siteTitle}`;
|
|
186
207
|
} catch (e) {
|
|
187
208
|
console.error("Failed to update document title", e);
|
|
188
209
|
}
|
package/src/site-kit/html.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { DEFAULT_TITLE, type PatchworkSiteOptions } from "./options.js";
|
|
2
2
|
import { resolveSyncServers, PRELOAD_WASM_ASSETS } from "./sync-servers.js";
|
|
3
3
|
import { ICON_SPECS } from "./icons.js";
|
|
4
4
|
|
|
@@ -16,7 +16,7 @@ export function escapeHtml(value: string): string {
|
|
|
16
16
|
|
|
17
17
|
/** Builds the generated index.html as a plain string — no bundler involved. */
|
|
18
18
|
export function buildHtml(options: PatchworkSiteOptions): string {
|
|
19
|
-
const title = options.title ??
|
|
19
|
+
const title = options.title ?? DEFAULT_TITLE;
|
|
20
20
|
const lang = (options.html && options.html.lang) || "en";
|
|
21
21
|
const entry = options.entry ?? "/src/main.ts";
|
|
22
22
|
const syncServers = resolveSyncServers(options);
|
package/src/site-kit/manifest.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { DEFAULT_TITLE, type PatchworkSiteOptions } from "./options.js";
|
|
2
2
|
import { ICON_SPECS } from "./icons.js";
|
|
3
3
|
|
|
4
4
|
/** Builds the generated manifest.webmanifest object — no bundler involved. */
|
|
5
5
|
export function buildManifest(
|
|
6
6
|
options: PatchworkSiteOptions
|
|
7
7
|
): Record<string, unknown> {
|
|
8
|
-
const title = options.title ??
|
|
8
|
+
const title = options.title ?? DEFAULT_TITLE;
|
|
9
9
|
const icons = !options.icons
|
|
10
10
|
? []
|
|
11
11
|
: ICON_SPECS.filter(
|
package/src/site-kit/options.ts
CHANGED
|
@@ -42,10 +42,24 @@ export type PatchworkSyncServersOptions = {
|
|
|
42
42
|
classic?: string | false;
|
|
43
43
|
} & PatchworkPrimarySyncServerOptions;
|
|
44
44
|
|
|
45
|
+
export const DEFAULT_TITLE = "Patchwork";
|
|
46
|
+
|
|
45
47
|
export interface PatchworkSiteOptions {
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Namespace for this site's IndexedDB databases and peer ids
|
|
50
|
+
* (-> __STORAGE_PREFIX__ define). Defaults to `"patchwork"`. Sites sharing
|
|
51
|
+
* an origin MUST use distinct prefixes.
|
|
52
|
+
*
|
|
53
|
+
* The tab and the shared automerge worker are separate bundles that have to
|
|
54
|
+
* open the same databases, so this is settable only here, where both of them
|
|
55
|
+
* receive it. Changing it on an existing site points it at empty storage.
|
|
56
|
+
*/
|
|
57
|
+
storagePrefix?: string;
|
|
58
|
+
/**
|
|
59
|
+
* This site's name: `<title>`, apple-mobile-web-app-title, manifest name,
|
|
60
|
+
* and — via the __SITE_TITLE__ define — the brand word the router appends to
|
|
61
|
+
* the document title as `"<doc> | <title>"`. Defaults to `"Patchwork"`.
|
|
62
|
+
*/
|
|
49
63
|
title?: string;
|
|
50
64
|
/** manifest short_name (defaults to title) */
|
|
51
65
|
shortName?: string;
|
package/src/types.ts
CHANGED
|
@@ -65,13 +65,14 @@ export interface PatchworkOptions {
|
|
|
65
65
|
createAccount?: AccountCreator;
|
|
66
66
|
|
|
67
67
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* for you), and used to namespace this site's storage and peer ids.
|
|
68
|
+
* This site's name, appended to the document title as `"<doc> | <title>"`
|
|
69
|
+
* when a document is open (the separator is provided for you).
|
|
71
70
|
*
|
|
72
|
-
* Defaults to the build-time `
|
|
71
|
+
* Defaults to the build-time `__SITE_TITLE__` define — the vite plugin's
|
|
72
|
+
* `title` option, which also names the html `<title>` and the manifest —
|
|
73
|
+
* then `"Patchwork"`.
|
|
73
74
|
*/
|
|
74
|
-
|
|
75
|
+
title?: string;
|
|
75
76
|
|
|
76
77
|
/** DOM id of the `<patchwork-view>` hosting the root tool. Defaults to "root". */
|
|
77
78
|
rootElementId?: string;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Plugin } from "vite";
|
|
2
2
|
import wasm from "vite-plugin-wasm";
|
|
3
|
+
import { DEFAULT_STORAGE_PREFIX } from "@inkandswitch/patchwork-bootloader/storage";
|
|
4
|
+
import { DEFAULT_TITLE } from "../site-kit/options.js";
|
|
3
5
|
import type { PatchworkVitePluginOptions } from "./patchwork-plugin.js";
|
|
4
6
|
import {
|
|
5
7
|
DEFAULT_SYNC_SERVERS,
|
|
@@ -9,7 +11,8 @@ import {
|
|
|
9
11
|
const CORS_HEADERS = { "Access-Control-Allow-Origin": "*" };
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
|
-
* Owns envPrefix, define (
|
|
14
|
+
* Owns envPrefix, define (__SITE_TITLE__/__STORAGE_PREFIX__/sync-server
|
|
15
|
+
* configuration),
|
|
13
16
|
* server/preview CORS defaults, worker format + the wasm plugin, and build
|
|
14
17
|
* defaults (firefox150 target, unminified, sourcemapped) — everything a site
|
|
15
18
|
* used to hand-write in its own vite.config.ts. Each is switched off
|
|
@@ -26,13 +29,16 @@ export function configPlugin(
|
|
|
26
29
|
options.syncServers && typeof options.syncServers.classic === "string"
|
|
27
30
|
? options.syncServers.classic
|
|
28
31
|
: DEFAULT_SYNC_SERVERS.classic;
|
|
32
|
+
// __STORAGE_PREFIX__ is defined unconditionally: the tab and the shared
|
|
33
|
+
// automerge worker resolve it separately, and only a define reaches both.
|
|
29
34
|
const define: Record<string, string> = {
|
|
30
35
|
__SYNC_SERVER__: JSON.stringify(primarySyncServer),
|
|
31
36
|
__CLASSIC_SYNC_SERVER__: JSON.stringify(classicSyncServer),
|
|
37
|
+
__SITE_TITLE__: JSON.stringify(options.title ?? DEFAULT_TITLE),
|
|
38
|
+
__STORAGE_PREFIX__: JSON.stringify(
|
|
39
|
+
options.storagePrefix ?? DEFAULT_STORAGE_PREFIX
|
|
40
|
+
),
|
|
32
41
|
};
|
|
33
|
-
if (options.siteName) {
|
|
34
|
-
define.__SITE_NAME__ = JSON.stringify(options.siteName);
|
|
35
|
-
}
|
|
36
42
|
|
|
37
43
|
return {
|
|
38
44
|
envPrefix: ["VITE_", "PATCHWORK_"],
|