@inkandswitch/patchwork 0.1.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.
- package/CHANGELOG.md +50 -0
- package/dist/client.d.ts +1 -1
- package/dist/createAccount.d.ts +2 -0
- package/dist/createAccount.js +18 -0
- package/dist/index.js +10 -20
- package/dist/types.d.ts +2 -3
- package/package.json +10 -5
- package/src/client.d.ts +1 -1
- package/src/createAccount.ts +31 -0
- package/src/index.ts +17 -33
- package/src/types.ts +6 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @inkandswitch/patchwork
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9e6e0e0: Add a `createAccount` setup option, create required account subdocuments before exposing a fresh account, and stop exposing the account handle as `window.accountDocHandle`.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [9e6e0e0]
|
|
12
|
+
- @inkandswitch/patchwork-plugins@1.1.0
|
|
13
|
+
- @inkandswitch/patchwork-elements@5.0.0
|
|
14
|
+
- @inkandswitch/patchwork-bootloader@0.5.3
|
|
15
|
+
|
|
16
|
+
## 0.2.1
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- f00dcb8: Add `repository` metadata pointing at inkandswitch/patchwork-system, so npm links each package to its source directory and can attest provenance when published from CI.
|
|
21
|
+
- Updated dependencies [f00dcb8]
|
|
22
|
+
- @inkandswitch/patchwork-bootloader@0.5.2
|
|
23
|
+
- @inkandswitch/patchwork-filesystem@0.2.5
|
|
24
|
+
- @inkandswitch/patchwork-elements@4.0.4
|
|
25
|
+
- @inkandswitch/patchwork-plugins@1.0.3
|
|
26
|
+
|
|
27
|
+
## 0.2.0
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- 2fffabe: `setup()` now uses `packageListURL` as given instead of letting `localStorage.systemPackageListURL` silently replace it. A site that wants a dev override resolves it itself and passes the result in:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const packageListURL =
|
|
35
|
+
new URLSearchParams(location.search).get("system-package-list") ||
|
|
36
|
+
localStorage.getItem("systemPackageListURL") ||
|
|
37
|
+
DEFAULT_PACKAGE_LIST;
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
This keeps the precedence in one place — the site — so a site can add its own override sources without fighting the library for priority.
|
|
41
|
+
|
|
42
|
+
### Patch Changes
|
|
43
|
+
|
|
44
|
+
- 6be3922: Wait for configured modules to load before routing the root view so registered frame tools are available for the initial route.
|
|
45
|
+
- 77bd37c: Expose the account document handle as `window.accountDocHandle` alongside `window.patchwork.account`.
|
|
46
|
+
- 5f70c14: Add `repository` metadata pointing at inkandswitch/patchwork-next, so npm links each package to its source directory and can attest provenance when published from CI.
|
|
47
|
+
- Updated dependencies [5f70c14]
|
|
48
|
+
- @inkandswitch/patchwork-bootloader@0.5.1
|
|
49
|
+
- @inkandswitch/patchwork-filesystem@0.2.4
|
|
50
|
+
- @inkandswitch/patchwork-elements@4.0.3
|
|
51
|
+
- @inkandswitch/patchwork-plugins@1.0.2
|
|
52
|
+
|
|
3
53
|
## 0.1.0
|
|
4
54
|
|
|
5
55
|
### Minor Changes
|
package/dist/client.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ declare global {
|
|
|
18
18
|
/**
|
|
19
19
|
* Comma-separated list of default tool-manifest sources the shell boots
|
|
20
20
|
* with. Each entry is an `automerge:` URL or a static `modules.json`
|
|
21
|
-
* URL.
|
|
21
|
+
* URL.
|
|
22
22
|
*/
|
|
23
23
|
readonly PATCHWORK_SYSTEM_PACKAGE_LIST_URL?: string;
|
|
24
24
|
/**
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createDocOfDatatype2, getRegistry, } from "@inkandswitch/patchwork-plugins";
|
|
2
|
+
export const createDefaultAccount = async (accountHandle, repo) => {
|
|
3
|
+
const fields = [
|
|
4
|
+
["rootFolderUrl", "folder"],
|
|
5
|
+
["moduleSettingsUrl", "patchwork:module-settings"],
|
|
6
|
+
["contactUrl", "contact"],
|
|
7
|
+
];
|
|
8
|
+
const registry = getRegistry("patchwork:datatype");
|
|
9
|
+
const subdocs = await Promise.all(fields.map(async ([field, datatypeId]) => {
|
|
10
|
+
const datatype = await registry.loadWhenReady(datatypeId);
|
|
11
|
+
const handle = await createDocOfDatatype2(datatype, repo);
|
|
12
|
+
return [field, handle.url];
|
|
13
|
+
}));
|
|
14
|
+
accountHandle.change((doc) => {
|
|
15
|
+
for (const [field, url] of subdocs)
|
|
16
|
+
doc[field] = url;
|
|
17
|
+
});
|
|
18
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* `@inkandswitch/patchwork-bootloader` directly, which does SW registration
|
|
18
18
|
* and the automerge-worker handoff and nothing else.
|
|
19
19
|
*/
|
|
20
|
-
import { MessageChannelNetworkAdapter,
|
|
20
|
+
import { MessageChannelNetworkAdapter, } from "@automerge/vanillajs/slim";
|
|
21
21
|
import * as Automerge from "@automerge/automerge/slim";
|
|
22
22
|
import * as AutomergeRepo from "@automerge/automerge-repo/slim";
|
|
23
23
|
import { ModuleWatcher } from "@inkandswitch/patchwork-filesystem";
|
|
@@ -30,6 +30,7 @@ import setupServiceWorker, { lifecycleLog, } from "@inkandswitch/patchwork-bootl
|
|
|
30
30
|
import debug from "debug";
|
|
31
31
|
import { createRepo, firstRepoPort, initWasm, removeAdapterFor, } from "./repo.js";
|
|
32
32
|
import { createRouter } from "./router.js";
|
|
33
|
+
import { createDefaultAccount } from "./createAccount.js";
|
|
33
34
|
const log = debug("patchwork:setup");
|
|
34
35
|
// ── Setup ────────────────────────────────────────────────────────────────
|
|
35
36
|
let setupCalled = false;
|
|
@@ -139,11 +140,16 @@ async function doSetup(options) {
|
|
|
139
140
|
const accountDocHandle = (await resolveAccountHandle(repo, {
|
|
140
141
|
storageKey: options.accountKey ?? "patchworkAccountURL",
|
|
141
142
|
hive,
|
|
143
|
+
createAccount: options.createAccount ?? createDefaultAccount,
|
|
142
144
|
}));
|
|
143
145
|
wireModuleSettings(accountDocHandle, moduleWatcher);
|
|
146
|
+
const toolsLoaded = moduleWatcher.doneLoading.then(() => log("doneLoading, tools registered:", getRegistry("patchwork:tool")
|
|
147
|
+
.all()
|
|
148
|
+
.map((t) => t.id)), (err) => console.error("doneLoading rejected:", err));
|
|
144
149
|
let router;
|
|
145
150
|
if (routing !== false) {
|
|
146
151
|
rootElement.style.visibility = "hidden";
|
|
152
|
+
await toolsLoaded;
|
|
147
153
|
router = createRouter({
|
|
148
154
|
rootElement,
|
|
149
155
|
repo,
|
|
@@ -151,9 +157,6 @@ async function doSetup(options) {
|
|
|
151
157
|
siteName,
|
|
152
158
|
});
|
|
153
159
|
}
|
|
154
|
-
const toolsLoaded = moduleWatcher.doneLoading.then(() => log("doneLoading, tools registered:", getRegistry("patchwork:tool")
|
|
155
|
-
.all()
|
|
156
|
-
.map((t) => t.id)), (err) => console.error("doneLoading rejected:", err));
|
|
157
160
|
installReveal(rootElement, router, toolsLoaded);
|
|
158
161
|
return {
|
|
159
162
|
repo,
|
|
@@ -215,25 +218,13 @@ function installReveal(rootElement, router, toolsLoaded) {
|
|
|
215
218
|
setTimeout(reveal, 12_000);
|
|
216
219
|
}
|
|
217
220
|
// ── Module sources ───────────────────────────────────────────────────────
|
|
218
|
-
function isValidModuleSource(source) {
|
|
219
|
-
return isValidAutomergeUrl(source) || /^(https?:\/\/|\.?\/)/.test(source);
|
|
220
|
-
}
|
|
221
221
|
/**
|
|
222
|
-
* The site's
|
|
223
|
-
*
|
|
224
|
-
* built-in bundle.
|
|
222
|
+
* The site's module-list sources. The site owns any dev overrides and passes
|
|
223
|
+
* the result in as `packageListURL`.
|
|
225
224
|
*/
|
|
226
225
|
function resolveDefaultModules(options) {
|
|
227
226
|
const configured = options.packageListURL ?? [];
|
|
228
227
|
const builtin = (Array.isArray(configured) ? configured : [configured]).filter(Boolean);
|
|
229
|
-
const override = globalThis.localStorage?.getItem("systemPackageListURL");
|
|
230
|
-
if (override && isValidModuleSource(override)) {
|
|
231
|
-
console.info(`using systemPackageListURL from localStorage: ${override}`);
|
|
232
|
-
return [override];
|
|
233
|
-
}
|
|
234
|
-
if (override) {
|
|
235
|
-
console.warn(`ignoring invalid systemPackageListURL in localStorage: ${override}`);
|
|
236
|
-
}
|
|
237
228
|
if (builtin.length === 0) {
|
|
238
229
|
throw new Error("patchwork.setup: no default module sources configured (set `packageListURL`)");
|
|
239
230
|
}
|
|
@@ -256,8 +247,7 @@ function onModuleLoaded(name, mod) {
|
|
|
256
247
|
registerPlugins(mod.plugins, name);
|
|
257
248
|
}
|
|
258
249
|
/**
|
|
259
|
-
*
|
|
260
|
-
* appear and feed it to the ModuleWatcher.
|
|
250
|
+
* Feed the account's module settings document to the ModuleWatcher.
|
|
261
251
|
*/
|
|
262
252
|
function wireModuleSettings(accountDocHandle, moduleWatcher) {
|
|
263
253
|
const wire = () => {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AutomergeUrl, DocHandle, Repo } from "@automerge/vanillajs/slim";
|
|
2
2
|
import type { AutomergeRepoKeyhive } from "@automerge/automerge-repo-keyhive";
|
|
3
3
|
import type { ModuleWatcher, HasPatchworkMetadata } from "@inkandswitch/patchwork-filesystem";
|
|
4
|
-
import type { AccountDoc } from "@inkandswitch/patchwork-plugins";
|
|
4
|
+
import type { AccountCreator, AccountDoc } from "@inkandswitch/patchwork-plugins";
|
|
5
5
|
import type { ServiceWorkerRepoChannelListener, SyncStateDocMessage } from "@inkandswitch/patchwork-bootloader/types";
|
|
6
6
|
import type * as pluginsNS from "@inkandswitch/patchwork-plugins";
|
|
7
7
|
export type PluginsApi = typeof pluginsNS;
|
|
@@ -36,8 +36,6 @@ export interface PatchworkOptions {
|
|
|
36
36
|
* The module URLs *inside* either kind of source may themselves be Automerge
|
|
37
37
|
* folder docs or plain HTTP(S) bundles, so deployment targets can be freely
|
|
38
38
|
* mixed.
|
|
39
|
-
*
|
|
40
|
-
* Overridable at runtime with `localStorage.systemPackageListURL`.
|
|
41
39
|
*/
|
|
42
40
|
packageListURL?: string | string[];
|
|
43
41
|
/**
|
|
@@ -47,6 +45,7 @@ export interface PatchworkOptions {
|
|
|
47
45
|
* other's accounts.
|
|
48
46
|
*/
|
|
49
47
|
accountKey?: string;
|
|
48
|
+
createAccount?: AccountCreator;
|
|
50
49
|
/**
|
|
51
50
|
* Brand word for this site: appended to the document title as
|
|
52
51
|
* `"<doc> | <name>"` when a document is open (the separator is provided
|
package/package.json
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkandswitch/patchwork",
|
|
3
|
-
"
|
|
3
|
+
"repository": {
|
|
4
|
+
"type": "git",
|
|
5
|
+
"url": "git+https://github.com/inkandswitch/patchwork-system.git",
|
|
6
|
+
"directory": "core/patchwork"
|
|
7
|
+
},
|
|
8
|
+
"version": "0.3.0",
|
|
4
9
|
"author": "Ink & Switch",
|
|
5
10
|
"type": "module",
|
|
6
11
|
"license": "MIT",
|
|
@@ -37,10 +42,10 @@
|
|
|
37
42
|
"debug": "^4.4.3",
|
|
38
43
|
"sharp": "^0.35.3",
|
|
39
44
|
"vite-plugin-wasm": "^3.6.0",
|
|
40
|
-
"@inkandswitch/patchwork-bootloader": "^0.5.
|
|
41
|
-
"@inkandswitch/patchwork-elements": "^
|
|
42
|
-
"@inkandswitch/patchwork-
|
|
43
|
-
"@inkandswitch/patchwork-
|
|
45
|
+
"@inkandswitch/patchwork-bootloader": "^0.5.3",
|
|
46
|
+
"@inkandswitch/patchwork-elements": "^5.0.0",
|
|
47
|
+
"@inkandswitch/patchwork-plugins": "^1.1.0",
|
|
48
|
+
"@inkandswitch/patchwork-filesystem": "^0.2.5",
|
|
44
49
|
"@inkandswitch/patchwork-providers": "^0.4.2"
|
|
45
50
|
},
|
|
46
51
|
"devDependencies": {
|
package/src/client.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ declare global {
|
|
|
18
18
|
/**
|
|
19
19
|
* Comma-separated list of default tool-manifest sources the shell boots
|
|
20
20
|
* with. Each entry is an `automerge:` URL or a static `modules.json`
|
|
21
|
-
* URL.
|
|
21
|
+
* URL.
|
|
22
22
|
*/
|
|
23
23
|
readonly PATCHWORK_SYSTEM_PACKAGE_LIST_URL?: string;
|
|
24
24
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AccountCreator,
|
|
3
|
+
type AccountDoc,
|
|
4
|
+
type DatatypeDescription,
|
|
5
|
+
createDocOfDatatype2,
|
|
6
|
+
getRegistry,
|
|
7
|
+
} from "@inkandswitch/patchwork-plugins";
|
|
8
|
+
|
|
9
|
+
export const createDefaultAccount: AccountCreator<AccountDoc> = async (
|
|
10
|
+
accountHandle,
|
|
11
|
+
repo
|
|
12
|
+
) => {
|
|
13
|
+
const fields = [
|
|
14
|
+
["rootFolderUrl", "folder"],
|
|
15
|
+
["moduleSettingsUrl", "patchwork:module-settings"],
|
|
16
|
+
["contactUrl", "contact"],
|
|
17
|
+
] as const;
|
|
18
|
+
const registry = getRegistry<DatatypeDescription>("patchwork:datatype");
|
|
19
|
+
|
|
20
|
+
const subdocs = await Promise.all(
|
|
21
|
+
fields.map(async ([field, datatypeId]) => {
|
|
22
|
+
const datatype = await registry.loadWhenReady(datatypeId);
|
|
23
|
+
const handle = await createDocOfDatatype2(datatype, repo);
|
|
24
|
+
return [field, handle.url] as const;
|
|
25
|
+
})
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
accountHandle.change((doc) => {
|
|
29
|
+
for (const [field, url] of subdocs) doc[field] = url;
|
|
30
|
+
});
|
|
31
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
type DocHandle,
|
|
23
23
|
MessageChannelNetworkAdapter,
|
|
24
24
|
Repo,
|
|
25
|
-
isValidAutomergeUrl,
|
|
26
25
|
} from "@automerge/vanillajs/slim";
|
|
27
26
|
import * as Automerge from "@automerge/automerge/slim";
|
|
28
27
|
import * as AutomergeRepo from "@automerge/automerge-repo/slim";
|
|
@@ -61,6 +60,7 @@ import {
|
|
|
61
60
|
removeAdapterFor,
|
|
62
61
|
} from "./repo.js";
|
|
63
62
|
import { createRouter, type Router } from "./router.js";
|
|
63
|
+
import { createDefaultAccount } from "./createAccount.js";
|
|
64
64
|
|
|
65
65
|
const log = debug("patchwork:setup");
|
|
66
66
|
|
|
@@ -222,21 +222,11 @@ async function doSetup(options: PatchworkOptions): Promise<Patchwork> {
|
|
|
222
222
|
const accountDocHandle = (await resolveAccountHandle(repo, {
|
|
223
223
|
storageKey: options.accountKey ?? "patchworkAccountURL",
|
|
224
224
|
hive,
|
|
225
|
+
createAccount: options.createAccount ?? createDefaultAccount,
|
|
225
226
|
})) as DocHandle<AccountDoc>;
|
|
226
227
|
|
|
227
228
|
wireModuleSettings(accountDocHandle, moduleWatcher);
|
|
228
229
|
|
|
229
|
-
let router: Router | undefined;
|
|
230
|
-
if (routing !== false) {
|
|
231
|
-
rootElement.style.visibility = "hidden";
|
|
232
|
-
router = createRouter({
|
|
233
|
-
rootElement,
|
|
234
|
-
repo,
|
|
235
|
-
accountDocHandle,
|
|
236
|
-
siteName,
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
|
|
240
230
|
const toolsLoaded = moduleWatcher.doneLoading.then(
|
|
241
231
|
() =>
|
|
242
232
|
log(
|
|
@@ -248,6 +238,18 @@ async function doSetup(options: PatchworkOptions): Promise<Patchwork> {
|
|
|
248
238
|
(err: unknown) => console.error("doneLoading rejected:", err)
|
|
249
239
|
);
|
|
250
240
|
|
|
241
|
+
let router: Router | undefined;
|
|
242
|
+
if (routing !== false) {
|
|
243
|
+
rootElement.style.visibility = "hidden";
|
|
244
|
+
await toolsLoaded;
|
|
245
|
+
router = createRouter({
|
|
246
|
+
rootElement,
|
|
247
|
+
repo,
|
|
248
|
+
accountDocHandle,
|
|
249
|
+
siteName,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
251
253
|
installReveal(rootElement, router, toolsLoaded);
|
|
252
254
|
|
|
253
255
|
return {
|
|
@@ -330,14 +332,9 @@ function installReveal(
|
|
|
330
332
|
|
|
331
333
|
// ── Module sources ───────────────────────────────────────────────────────
|
|
332
334
|
|
|
333
|
-
function isValidModuleSource(source: string): boolean {
|
|
334
|
-
return isValidAutomergeUrl(source) || /^(https?:\/\/|\.?\/)/.test(source);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
335
|
/**
|
|
338
|
-
* The site's
|
|
339
|
-
*
|
|
340
|
-
* built-in bundle.
|
|
336
|
+
* The site's module-list sources. The site owns any dev overrides and passes
|
|
337
|
+
* the result in as `packageListURL`.
|
|
341
338
|
*/
|
|
342
339
|
function resolveDefaultModules(options: PatchworkOptions): string[] {
|
|
343
340
|
const configured = options.packageListURL ?? [];
|
|
@@ -345,18 +342,6 @@ function resolveDefaultModules(options: PatchworkOptions): string[] {
|
|
|
345
342
|
Array.isArray(configured) ? configured : [configured]
|
|
346
343
|
).filter(Boolean);
|
|
347
344
|
|
|
348
|
-
const override = globalThis.localStorage?.getItem("systemPackageListURL");
|
|
349
|
-
|
|
350
|
-
if (override && isValidModuleSource(override)) {
|
|
351
|
-
console.info(`using systemPackageListURL from localStorage: ${override}`);
|
|
352
|
-
return [override];
|
|
353
|
-
}
|
|
354
|
-
if (override) {
|
|
355
|
-
console.warn(
|
|
356
|
-
`ignoring invalid systemPackageListURL in localStorage: ${override}`
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
345
|
if (builtin.length === 0) {
|
|
361
346
|
throw new Error(
|
|
362
347
|
"patchwork.setup: no default module sources configured (set `packageListURL`)"
|
|
@@ -389,8 +374,7 @@ function onModuleLoaded(name: string, mod: any): void {
|
|
|
389
374
|
}
|
|
390
375
|
|
|
391
376
|
/**
|
|
392
|
-
*
|
|
393
|
-
* appear and feed it to the ModuleWatcher.
|
|
377
|
+
* Feed the account's module settings document to the ModuleWatcher.
|
|
394
378
|
*/
|
|
395
379
|
function wireModuleSettings(
|
|
396
380
|
accountDocHandle: DocHandle<AccountDoc>,
|
package/src/types.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AutomergeUrl,
|
|
3
|
-
DocHandle,
|
|
4
|
-
Repo,
|
|
5
|
-
} from "@automerge/vanillajs/slim";
|
|
1
|
+
import type { AutomergeUrl, DocHandle, Repo } from "@automerge/vanillajs/slim";
|
|
6
2
|
import type { AutomergeRepoKeyhive } from "@automerge/automerge-repo-keyhive";
|
|
7
3
|
import type {
|
|
8
4
|
ModuleWatcher,
|
|
9
5
|
HasPatchworkMetadata,
|
|
10
6
|
} from "@inkandswitch/patchwork-filesystem";
|
|
11
|
-
import type {
|
|
7
|
+
import type {
|
|
8
|
+
AccountCreator,
|
|
9
|
+
AccountDoc,
|
|
10
|
+
} from "@inkandswitch/patchwork-plugins";
|
|
12
11
|
import type {
|
|
13
12
|
ServiceWorkerRepoChannelListener,
|
|
14
13
|
SyncStateDocMessage,
|
|
@@ -53,8 +52,6 @@ export interface PatchworkOptions {
|
|
|
53
52
|
* The module URLs *inside* either kind of source may themselves be Automerge
|
|
54
53
|
* folder docs or plain HTTP(S) bundles, so deployment targets can be freely
|
|
55
54
|
* mixed.
|
|
56
|
-
*
|
|
57
|
-
* Overridable at runtime with `localStorage.systemPackageListURL`.
|
|
58
55
|
*/
|
|
59
56
|
packageListURL?: string | string[];
|
|
60
57
|
|
|
@@ -65,6 +62,7 @@ export interface PatchworkOptions {
|
|
|
65
62
|
* other's accounts.
|
|
66
63
|
*/
|
|
67
64
|
accountKey?: string;
|
|
65
|
+
createAccount?: AccountCreator;
|
|
68
66
|
|
|
69
67
|
/**
|
|
70
68
|
* Brand word for this site: appended to the document title as
|