@tiddlywiki/mws 0.0.19 → 0.0.21
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/README.md +4 -0
- package/dist/server.d.ts +15 -5
- package/dist/server.js +194 -88
- package/dist/server.js.map +1 -1
- package/editions/mws-docs/tiddlers/$__StoryList.tid +2 -1
- package/package.json +1 -1
- package/react-user-mgmt/public/main.js +25 -25
- package/react-user-mgmt/public/main.js.map +4 -4
- package/react-user-mgmt/public/stats.json +1 -1
- package/scripts.mjs +2 -2
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# MultiWikiServer
|
|
2
2
|
|
|
3
|
+
**This is not ready for primetime. Do not use it to protect feelings or intellectual property.**
|
|
4
|
+
|
|
5
|
+
*You really shouldn't be using it to store anything important yet. It is still in the VERY early stages of development and we don't even have proper tests verifying the security mechanisms yet. So please try it out, kick the tires, import your multi-gigabyte wikis and play around with it, but don't actually store anything you want to keep. We'll hopefully get there soon, but even the database schema itself is still in flux as we figure out what the best layout is for all the stuff we have planned.*
|
|
6
|
+
|
|
3
7
|
Multiple users, multiple wikis for TiddlyWiki.
|
|
4
8
|
|
|
5
9
|
- Bag & Recipe system for storing tiddlers.
|
package/dist/server.d.ts
CHANGED
|
@@ -228,6 +228,10 @@ declare class StartupCommander {
|
|
|
228
228
|
$tw: TW;
|
|
229
229
|
PasswordService: PasswordService;
|
|
230
230
|
fieldModules: Record<string, TiddlerFieldModule>;
|
|
231
|
+
versions: {
|
|
232
|
+
tiddlywiki: any;
|
|
233
|
+
mws: string;
|
|
234
|
+
};
|
|
231
235
|
constructor(config: MWSConfig, $tw: TW, PasswordService: PasswordService);
|
|
232
236
|
init(): Promise<void>;
|
|
233
237
|
wikiPath: string;
|
|
@@ -333,6 +337,7 @@ declare class StateObject<B extends BodyFormat = BodyFormat, M extends AllowedMe
|
|
|
333
337
|
private engine;
|
|
334
338
|
config: SiteConfig;
|
|
335
339
|
PasswordService: PasswordService;
|
|
340
|
+
get pathPrefix(): string;
|
|
336
341
|
constructor(streamer: Streamer,
|
|
337
342
|
/** The array of Route tree nodes the request matched. */
|
|
338
343
|
routePath: RouteMatch[],
|
|
@@ -554,6 +559,9 @@ declare const Z2: Z2<FieldTypeGroups>;
|
|
|
554
559
|
declare global {
|
|
555
560
|
type PrismaTxnClient = Omit<Commander["engine"], "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends">;
|
|
556
561
|
type ART<T extends (...args: any) => any> = Awaited<ReturnType<T>>;
|
|
562
|
+
type Complete<T> = {
|
|
563
|
+
[K in keyof T]-?: T[K];
|
|
564
|
+
};
|
|
557
565
|
/**
|
|
558
566
|
* This primarily makes sure that positional arguments are used correctly
|
|
559
567
|
* (so you can't switch a title and bag_name around).
|
|
@@ -774,10 +782,8 @@ declare class StreamerState {
|
|
|
774
782
|
}
|
|
775
783
|
|
|
776
784
|
declare function startupCache(rootRoute: rootRoute, commander: Commander): Promise<{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
tiddlerFiles: Map<string, string>;
|
|
780
|
-
tiddlerHashes: Map<string, string>;
|
|
785
|
+
tiddlerFiles: Map<any, any>;
|
|
786
|
+
tiddlerHashes: Map<any, any>;
|
|
781
787
|
}>;
|
|
782
788
|
type CacheState = ART<typeof startupCache>;
|
|
783
789
|
|
|
@@ -797,6 +803,10 @@ declare class Router {
|
|
|
797
803
|
engine: Commander["engine"];
|
|
798
804
|
private SessionManager;
|
|
799
805
|
PasswordService: Commander["PasswordService"];
|
|
806
|
+
versions: {
|
|
807
|
+
tiddlywiki: any;
|
|
808
|
+
mws: string;
|
|
809
|
+
};
|
|
800
810
|
fieldModules: Commander["$tw"]["Tiddler"]["fieldModules"];
|
|
801
811
|
AttachmentService: Commander["AttachmentService"];
|
|
802
812
|
constructor(rootRoute: rootRoute$1, commander: Commander, tiddlerCache: CacheState);
|
|
@@ -888,7 +898,7 @@ interface MWSConfigConfig {
|
|
|
888
898
|
readonly pathPrefix?: string;
|
|
889
899
|
readonly enablePluginCache?: boolean;
|
|
890
900
|
}
|
|
891
|
-
interface SiteConfig extends MWSConfigConfig {
|
|
901
|
+
interface SiteConfig extends Complete<MWSConfigConfig> {
|
|
892
902
|
wikiPath: string;
|
|
893
903
|
attachmentSizeLimit: number;
|
|
894
904
|
attachmentsEnabled: boolean;
|
package/dist/server.js
CHANGED
|
@@ -152,8 +152,8 @@ var init_global = __esm({
|
|
|
152
152
|
});
|
|
153
153
|
};
|
|
154
154
|
__Result = class __Result {
|
|
155
|
-
constructor(
|
|
156
|
-
this.ok =
|
|
155
|
+
constructor(ok7, error, value) {
|
|
156
|
+
this.ok = ok7;
|
|
157
157
|
this.error = error;
|
|
158
158
|
this.value = value;
|
|
159
159
|
}
|
|
@@ -1621,7 +1621,7 @@ var require_destroy = __commonJS({
|
|
|
1621
1621
|
"node_modules/destroy/index.js"(exports, module2) {
|
|
1622
1622
|
"use strict";
|
|
1623
1623
|
init_esm_shims();
|
|
1624
|
-
var
|
|
1624
|
+
var EventEmitter2 = __require("events").EventEmitter;
|
|
1625
1625
|
var ReadStream = __require("fs").ReadStream;
|
|
1626
1626
|
var Stream = __require("stream");
|
|
1627
1627
|
var Zlib = __require("zlib");
|
|
@@ -1688,7 +1688,7 @@ var require_destroy = __commonJS({
|
|
|
1688
1688
|
}
|
|
1689
1689
|
__name(hasDestroy, "hasDestroy");
|
|
1690
1690
|
function isEventEmitter(val) {
|
|
1691
|
-
return val instanceof
|
|
1691
|
+
return val instanceof EventEmitter2;
|
|
1692
1692
|
}
|
|
1693
1693
|
__name(isEventEmitter, "isEventEmitter");
|
|
1694
1694
|
function isFsReadStream(stream) {
|
|
@@ -11569,7 +11569,8 @@ var init_data_checks = __esm({
|
|
|
11569
11569
|
init_esm_shims();
|
|
11570
11570
|
init_utils();
|
|
11571
11571
|
_DataChecks = class _DataChecks {
|
|
11572
|
-
constructor(
|
|
11572
|
+
constructor(config) {
|
|
11573
|
+
this.config = config;
|
|
11573
11574
|
this.isPermissionName = /* @__PURE__ */ __name((e) => {
|
|
11574
11575
|
const test2 = e;
|
|
11575
11576
|
switch (test2) {
|
|
@@ -11586,8 +11587,6 @@ var init_data_checks = __esm({
|
|
|
11586
11587
|
this.permissions = { READ: "READ", WRITE: "WRITE", ADMIN: "ADMIN" };
|
|
11587
11588
|
this.isEntityType = /* @__PURE__ */ __name((x) => ["recipe", "bag"].includes(x), "isEntityType");
|
|
11588
11589
|
this.entityTypes = { recipe: "recipe", bag: "bag" };
|
|
11589
|
-
this.allowAnonReads = options.allowAnonReads;
|
|
11590
|
-
this.allowAnonWrites = options.allowAnonWrites;
|
|
11591
11590
|
}
|
|
11592
11591
|
okTiddlerFields(tiddlerFields) {
|
|
11593
11592
|
ok2(tiddlerFields.title, "title must not be empty");
|
|
@@ -11648,17 +11647,11 @@ var init_data_checks = __esm({
|
|
|
11648
11647
|
].filter(truthy);
|
|
11649
11648
|
}
|
|
11650
11649
|
getWhereACL({ permission, user_id, role_ids }) {
|
|
11651
|
-
const { allowAnonReads, allowAnonWrites } = this;
|
|
11652
|
-
const anonRead = allowAnonReads && permission === "READ";
|
|
11653
|
-
const anonWrite = allowAnonWrites && permission === "WRITE";
|
|
11654
|
-
const allowAnon = anonRead || anonWrite;
|
|
11655
11650
|
const allperms = ["READ", "WRITE", "ADMIN"];
|
|
11656
11651
|
const index = allperms.indexOf(permission);
|
|
11657
11652
|
if (index === -1) throw new Error("Invalid permission");
|
|
11658
11653
|
const checkPerms = allperms.slice(index);
|
|
11659
11654
|
return [
|
|
11660
|
-
// allow unowned for any user (conditional for anon reads)
|
|
11661
|
-
(user_id || allowAnon) && { acl: { none: {} }, owner_id: null },
|
|
11662
11655
|
// allow owner for user
|
|
11663
11656
|
user_id && { owner_id: { equals: user_id, not: null } },
|
|
11664
11657
|
// allow acl for user
|
|
@@ -11669,7 +11662,9 @@ var init_data_checks = __esm({
|
|
|
11669
11662
|
role_id: { in: role_ids }
|
|
11670
11663
|
}
|
|
11671
11664
|
}
|
|
11672
|
-
}
|
|
11665
|
+
},
|
|
11666
|
+
{ owner_id: { equals: null, not: null } }
|
|
11667
|
+
// dud to make sure that at least one condition exists
|
|
11673
11668
|
].filter(truthy);
|
|
11674
11669
|
}
|
|
11675
11670
|
};
|
|
@@ -11814,8 +11809,9 @@ var init_streamer = __esm({
|
|
|
11814
11809
|
if (error === STREAM_ENDED) return;
|
|
11815
11810
|
const tag = this.urlInfo.href;
|
|
11816
11811
|
console.error(tag, error);
|
|
11817
|
-
if (!this.headersSent)
|
|
11812
|
+
if (!this.headersSent) {
|
|
11818
11813
|
this.sendEmpty(500, { "x-reason": "Internal Server Error (catcher)" });
|
|
11814
|
+
}
|
|
11819
11815
|
}, "catcher");
|
|
11820
11816
|
this.readBody = /* @__PURE__ */ __name(() => new Promise((resolve13) => {
|
|
11821
11817
|
const chunks = [];
|
|
@@ -12178,6 +12174,9 @@ var init_StateObject = __esm({
|
|
|
12178
12174
|
get method() {
|
|
12179
12175
|
return super.method;
|
|
12180
12176
|
}
|
|
12177
|
+
get pathPrefix() {
|
|
12178
|
+
return this.config.pathPrefix;
|
|
12179
|
+
}
|
|
12181
12180
|
okUser() {
|
|
12182
12181
|
if (!this.user.isLoggedIn) throw "User not authenticated";
|
|
12183
12182
|
}
|
|
@@ -12406,8 +12405,8 @@ var init_manager_recipes = __esm({
|
|
|
12406
12405
|
where: isAdmin ? void 0 : {
|
|
12407
12406
|
OR: [
|
|
12408
12407
|
{ recipe_bags: { every: { bag: { OR } } } },
|
|
12409
|
-
{ owner_id: { equals: user_id, not: null } }
|
|
12410
|
-
]
|
|
12408
|
+
user_id && { owner_id: { equals: user_id, not: null } }
|
|
12409
|
+
].filter((e) => e)
|
|
12411
12410
|
}
|
|
12412
12411
|
});
|
|
12413
12412
|
const userListUser = !isAdmin && await prisma.users.findMany({
|
|
@@ -12428,7 +12427,8 @@ var init_manager_recipes = __esm({
|
|
|
12428
12427
|
username,
|
|
12429
12428
|
isLoggedIn: state.user.isLoggedIn,
|
|
12430
12429
|
allowAnonReads: state.config.allowAnonReads,
|
|
12431
|
-
allowAnonWrites: state.config.allowAnonWrites
|
|
12430
|
+
allowAnonWrites: state.config.allowAnonWrites,
|
|
12431
|
+
versions: state.router.versions
|
|
12432
12432
|
};
|
|
12433
12433
|
});
|
|
12434
12434
|
this.recipe_create = zodManage((z5) => z5.object({
|
|
@@ -12585,8 +12585,7 @@ var init_manager_recipes = __esm({
|
|
|
12585
12585
|
});
|
|
12586
12586
|
return null;
|
|
12587
12587
|
});
|
|
12588
|
-
|
|
12589
|
-
this.checks = new DataChecks({ allowAnonReads, allowAnonWrites });
|
|
12588
|
+
this.checks = new DataChecks(config);
|
|
12590
12589
|
}
|
|
12591
12590
|
static defineRoutes(root, config) {
|
|
12592
12591
|
registerZodRoutes(root, new _RecipeManager(config), Object.keys(RecipeKeyMap));
|
|
@@ -13190,13 +13189,15 @@ var init_TiddlerStore = __esm({
|
|
|
13190
13189
|
recipe_bags: {
|
|
13191
13190
|
// we're saving to the top most bag
|
|
13192
13191
|
where: { position: 0 },
|
|
13193
|
-
select: { bag: { select: { bag_id: true, bag_name: true } } }
|
|
13192
|
+
select: { bag: { select: { bag_id: true, bag_name: true, is_plugin: true } } }
|
|
13194
13193
|
}
|
|
13195
13194
|
}
|
|
13196
13195
|
});
|
|
13197
13196
|
if (!recipe) throw new UserError("Recipe not found");
|
|
13198
13197
|
const bag_name = recipe.recipe_bags[0]?.bag.bag_name;
|
|
13199
13198
|
if (!bag_name) throw new UserError("Recipe has no bag at position 0");
|
|
13199
|
+
if (recipe.recipe_bags[0]?.bag.is_plugin)
|
|
13200
|
+
throw new UserError("Saving to plugin bags is not currently supported. Please use a normal bag. This error occurs if a plugin bag is at the top of the recipe.\n");
|
|
13200
13201
|
var { tiddler_id } = await this.saveBagTiddlerFields(tiddlerFields, bag_name, attachment_hash);
|
|
13201
13202
|
return { tiddler_id, bag_name };
|
|
13202
13203
|
}
|
|
@@ -14366,8 +14367,7 @@ var init_bag_file_server = __esm({
|
|
|
14366
14367
|
fields: true
|
|
14367
14368
|
},
|
|
14368
14369
|
where: {
|
|
14369
|
-
is_deleted: false
|
|
14370
|
-
bag: { is_plugin: false }
|
|
14370
|
+
is_deleted: false
|
|
14371
14371
|
}
|
|
14372
14372
|
}
|
|
14373
14373
|
}
|
|
@@ -14677,58 +14677,62 @@ var init_router_tiddlers = __esm({
|
|
|
14677
14677
|
|
|
14678
14678
|
// src/routes/tw-routes.ts
|
|
14679
14679
|
import { TiddlyWiki } from "tiddlywiki";
|
|
14680
|
-
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14680
|
+
import { basename } from "path";
|
|
14681
|
+
import EventEmitter from "events";
|
|
14682
|
+
function DocsRoute(rootRoute2, mountPath, singleFile) {
|
|
14683
|
+
const $tw2 = TiddlyWiki();
|
|
14684
|
+
const last = basename(mountPath);
|
|
14685
|
+
$tw2.preloadTiddler({
|
|
14686
|
+
title: "$:/config/tiddlyweb/host",
|
|
14687
|
+
// if the page url doesn't end with a slash, we have to include the basename here
|
|
14688
|
+
text: singleFile ? last + "/" : ""
|
|
14689
|
+
});
|
|
14690
|
+
$tw2.boot.argv = [
|
|
14691
|
+
"+plugins/tiddlywiki/tiddlyweb",
|
|
14692
|
+
"+plugins/tiddlywiki/filesystem",
|
|
14693
|
+
// relative to the cwd
|
|
14694
|
+
"./editions/mws-docs"
|
|
14695
|
+
];
|
|
14696
|
+
const hooks = new EventEmitter();
|
|
14697
|
+
$tw2.boot.boot(() => {
|
|
14698
|
+
const Server2 = $tw2.modules.execute("$:/core/modules/server/server.js", "tw-test.ts").Server;
|
|
14699
|
+
const twserver = new Server2({
|
|
14700
|
+
wiki: $tw2.wiki,
|
|
14701
|
+
variables: {
|
|
14702
|
+
// path prefix gets set per-request
|
|
14703
|
+
"path-prefix": "",
|
|
14704
|
+
"root-tiddler": "$:/core/save/all"
|
|
14705
|
+
},
|
|
14706
|
+
routes: []
|
|
14707
|
+
});
|
|
14708
|
+
$tw2.hooks.invokeHook("th-server-command-post-start", twserver, null, "mws");
|
|
14709
|
+
hooks.on("req", (req, res, pathPrefix) => {
|
|
14710
|
+
twserver.requestHandler(req, res, { pathPrefix });
|
|
14711
|
+
});
|
|
14712
|
+
});
|
|
14713
|
+
rootRoute2.defineRoute({
|
|
14684
14714
|
method: ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE"],
|
|
14685
14715
|
bodyFormat: "stream",
|
|
14686
|
-
path:
|
|
14716
|
+
path: new RegExp("^" + mountPath + "(/|$)"),
|
|
14687
14717
|
pathParams: []
|
|
14688
14718
|
}, async (state) => {
|
|
14689
|
-
if (state.urlInfo.pathname ===
|
|
14719
|
+
if (singleFile && state.urlInfo.pathname === mountPath + "/")
|
|
14720
|
+
throw state.redirect(mountPath);
|
|
14721
|
+
if (!singleFile && state.urlInfo.pathname === mountPath)
|
|
14722
|
+
throw state.redirect(mountPath + "/");
|
|
14690
14723
|
const { req, res } = state.streamer;
|
|
14691
|
-
if (req.url ===
|
|
14692
|
-
|
|
14693
|
-
|
|
14724
|
+
if (req.url === state.pathPrefix + mountPath)
|
|
14725
|
+
req.url = state.pathPrefix + mountPath + "/";
|
|
14726
|
+
const prom = new Promise((r, c) => res.on("finish", r).on("error", c));
|
|
14727
|
+
hooks.emit("req", req, res, state.pathPrefix + mountPath);
|
|
14728
|
+
return prom;
|
|
14694
14729
|
});
|
|
14695
14730
|
}
|
|
14696
|
-
var _TWRouter, TWRouter;
|
|
14697
14731
|
var init_tw_routes = __esm({
|
|
14698
14732
|
"src/routes/tw-routes.ts"() {
|
|
14699
14733
|
"use strict";
|
|
14700
14734
|
init_esm_shims();
|
|
14701
14735
|
__name(DocsRoute, "DocsRoute");
|
|
14702
|
-
_TWRouter = class _TWRouter {
|
|
14703
|
-
constructor(config) {
|
|
14704
|
-
this.config = config;
|
|
14705
|
-
this.$tw = TiddlyWiki();
|
|
14706
|
-
this.$tw.boot.argv = [
|
|
14707
|
-
"+plugins/tiddlywiki/tiddlyweb",
|
|
14708
|
-
"+plugins/tiddlywiki/filesystem",
|
|
14709
|
-
"./editions/mws-docs"
|
|
14710
|
-
];
|
|
14711
|
-
this.$tw.preloadTiddler({
|
|
14712
|
-
text: "$protocol$//$host$" + this.config.pathPrefix + "/mws/",
|
|
14713
|
-
title: "$:/config/tiddlyweb/host"
|
|
14714
|
-
});
|
|
14715
|
-
this.$tw.boot.boot(() => {
|
|
14716
|
-
const Server2 = this.$tw.modules.execute("$:/core/modules/server/server.js", "tw-test.ts").Server;
|
|
14717
|
-
this.twserver = new Server2({
|
|
14718
|
-
wiki: this.$tw.wiki,
|
|
14719
|
-
variables: {
|
|
14720
|
-
// do not use a trailing slash
|
|
14721
|
-
"path-prefix": this.config.pathPrefix + "/mws",
|
|
14722
|
-
"root-tiddler": "$:/core/save/all"
|
|
14723
|
-
},
|
|
14724
|
-
routes: []
|
|
14725
|
-
});
|
|
14726
|
-
this.$tw.hooks.invokeHook("th-server-command-post-start", this.twserver, null, "mws");
|
|
14727
|
-
});
|
|
14728
|
-
}
|
|
14729
|
-
};
|
|
14730
|
-
__name(_TWRouter, "TWRouter");
|
|
14731
|
-
TWRouter = _TWRouter;
|
|
14732
14736
|
}
|
|
14733
14737
|
});
|
|
14734
14738
|
|
|
@@ -14748,7 +14752,7 @@ var init_ = __esm({
|
|
|
14748
14752
|
// src/routes/index.ts
|
|
14749
14753
|
import { Prisma } from "@prisma/client";
|
|
14750
14754
|
async function RootRoute(root, config) {
|
|
14751
|
-
if (process.env.ENABLE_DOCS_ROUTE) DocsRoute(root,
|
|
14755
|
+
if (process.env.ENABLE_DOCS_ROUTE) DocsRoute(root, "/mws-docs", false);
|
|
14752
14756
|
TiddlerRouter.defineRoutes(root);
|
|
14753
14757
|
ManagerRoutes(root, config);
|
|
14754
14758
|
if (process.env.ENABLE_UNSAFE_PRISMA_ROUTE)
|
|
@@ -14991,21 +14995,12 @@ var init_setupDevServer = __esm({
|
|
|
14991
14995
|
});
|
|
14992
14996
|
|
|
14993
14997
|
// src/routes/cache.ts
|
|
14994
|
-
import { ok as ok4 } from "assert";
|
|
14995
14998
|
import * as fs2 from "fs";
|
|
14996
14999
|
import * as path3 from "path";
|
|
14997
15000
|
import * as crypto from "crypto";
|
|
14998
15001
|
async function startupCache(rootRoute2, commander2) {
|
|
14999
15002
|
const $tw2 = commander2.$tw;
|
|
15000
|
-
const
|
|
15001
|
-
const coreTiddler = $tw2.loadPluginFolder($tw2.boot.corePath);
|
|
15002
|
-
ok4(coreTiddler);
|
|
15003
|
-
const tiddlerMemoryCache = new Map([
|
|
15004
|
-
...bootTiddlers.map((e) => e.title && [e.title, JSON.stringify(e)]).filter(truthy),
|
|
15005
|
-
[coreTiddler.title, JSON.stringify(coreTiddler)]
|
|
15006
|
-
]);
|
|
15007
|
-
const tiddlerFileCache = /* @__PURE__ */ new Map();
|
|
15008
|
-
const { tiddlerFiles, tiddlerHashes } = await importTW5(path3.join($tw2.boot.corePath, ".."), commander2.cachePath, "client", $tw2);
|
|
15003
|
+
const { tiddlerFiles, tiddlerHashes } = commander2.siteConfig.enablePluginCache ? await importTW5(path3.join($tw2.boot.corePath, ".."), commander2.cachePath, "client", $tw2) : { tiddlerFiles: /* @__PURE__ */ new Map(), tiddlerHashes: /* @__PURE__ */ new Map() };
|
|
15009
15004
|
rootRoute2.defineRoute({
|
|
15010
15005
|
method: ["GET", "HEAD"],
|
|
15011
15006
|
path: /^\/\$cache\/(.*)\/plugin\.js$/,
|
|
@@ -15020,7 +15015,7 @@ async function startupCache(rootRoute2, commander2) {
|
|
|
15020
15015
|
reqpath: state.pathParams.plugin + "/plugin.js"
|
|
15021
15016
|
});
|
|
15022
15017
|
});
|
|
15023
|
-
return {
|
|
15018
|
+
return { tiddlerFiles, tiddlerHashes };
|
|
15024
15019
|
}
|
|
15025
15020
|
async function importTW5(twFolder, cacheFolder, type, $tw2) {
|
|
15026
15021
|
const readLevel = /* @__PURE__ */ __name((d) => {
|
|
@@ -15160,6 +15155,7 @@ var init_router = __esm({
|
|
|
15160
15155
|
this.PasswordService = commander2.PasswordService;
|
|
15161
15156
|
this.fieldModules = commander2.$tw.Tiddler.fieldModules;
|
|
15162
15157
|
this.AttachmentService = commander2.AttachmentService;
|
|
15158
|
+
this.versions = commander2.versions;
|
|
15163
15159
|
}
|
|
15164
15160
|
static async makeRouter(commander2, enableDevServer) {
|
|
15165
15161
|
const sendDevServer = await setupDevServer(enableDevServer, commander2.siteConfig.pathPrefix);
|
|
@@ -15180,14 +15176,14 @@ var init_router = __esm({
|
|
|
15180
15176
|
return this.siteConfig.pathPrefix;
|
|
15181
15177
|
}
|
|
15182
15178
|
handleIncomingRequest(req, res) {
|
|
15183
|
-
const [
|
|
15179
|
+
const [ok7, err, streamer] = function() {
|
|
15184
15180
|
try {
|
|
15185
15181
|
return [true, void 0, new Streamer(req, res, this)];
|
|
15186
15182
|
} catch (e) {
|
|
15187
15183
|
return [false, e, void 0];
|
|
15188
15184
|
}
|
|
15189
15185
|
}.call(this);
|
|
15190
|
-
if (!
|
|
15186
|
+
if (!ok7) {
|
|
15191
15187
|
if (err === STREAM_ENDED) return;
|
|
15192
15188
|
res.writeHead(500, { "x-reason": "handle incoming request" }).end();
|
|
15193
15189
|
throw err;
|
|
@@ -15341,8 +15337,10 @@ var init_router = __esm({
|
|
|
15341
15337
|
if (error === STREAM_ENDED) {
|
|
15342
15338
|
return error;
|
|
15343
15339
|
} else if (typeof error === "string") {
|
|
15340
|
+
console.log(error);
|
|
15344
15341
|
return state.sendString(400, { "x-reason": "zod-handler" }, error, "utf8");
|
|
15345
15342
|
} else if (error instanceof Error && error.name === "UserError") {
|
|
15343
|
+
console.log(error.stack);
|
|
15346
15344
|
return state.sendString(400, { "x-reason": "user-error" }, error.message, "utf8");
|
|
15347
15345
|
} else {
|
|
15348
15346
|
throw error;
|
|
@@ -15358,7 +15356,7 @@ var init_router = __esm({
|
|
|
15358
15356
|
|
|
15359
15357
|
// src/tiddlywiki.ts
|
|
15360
15358
|
import { TiddlyWiki as TiddlyWiki2 } from "tiddlywiki";
|
|
15361
|
-
import { ok as
|
|
15359
|
+
import { ok as ok4 } from "node:assert";
|
|
15362
15360
|
async function bootTiddlyWiki(wikiPath) {
|
|
15363
15361
|
const $tw2 = TiddlyWiki2();
|
|
15364
15362
|
$tw2.utils.eachAsync = eachAsync;
|
|
@@ -15402,7 +15400,7 @@ async function bootTiddlyWiki(wikiPath) {
|
|
|
15402
15400
|
// this should be inert
|
|
15403
15401
|
];
|
|
15404
15402
|
await new Promise((resolve13) => $tw2.boot.boot(resolve13));
|
|
15405
|
-
|
|
15403
|
+
ok4(!$tw2.boot.tasks.readBrowserTiddlers);
|
|
15406
15404
|
return $tw2;
|
|
15407
15405
|
}
|
|
15408
15406
|
var init_tiddlywiki = __esm({
|
|
@@ -16887,7 +16885,7 @@ var require_command = __commonJS({
|
|
|
16887
16885
|
"node_modules/commander/lib/command.js"(exports) {
|
|
16888
16886
|
"use strict";
|
|
16889
16887
|
init_esm_shims();
|
|
16890
|
-
var
|
|
16888
|
+
var EventEmitter2 = __require("node:events").EventEmitter;
|
|
16891
16889
|
var childProcess = __require("node:child_process");
|
|
16892
16890
|
var path6 = __require("node:path");
|
|
16893
16891
|
var fs4 = __require("node:fs");
|
|
@@ -16897,7 +16895,7 @@ var require_command = __commonJS({
|
|
|
16897
16895
|
var { Help: Help2, stripColor } = require_help();
|
|
16898
16896
|
var { Option: Option2, DualOptions } = require_option();
|
|
16899
16897
|
var { suggestSimilar } = require_suggestSimilar();
|
|
16900
|
-
var _Command12 = class _Command12 extends
|
|
16898
|
+
var _Command12 = class _Command12 extends EventEmitter2 {
|
|
16901
16899
|
/**
|
|
16902
16900
|
* Initialize a new `Command`.
|
|
16903
16901
|
*
|
|
@@ -19817,7 +19815,7 @@ __export(listen_exports, {
|
|
|
19817
19815
|
ListenerHTTPS: () => ListenerHTTPS,
|
|
19818
19816
|
info: () => info7
|
|
19819
19817
|
});
|
|
19820
|
-
import { ok as
|
|
19818
|
+
import { ok as ok5 } from "assert";
|
|
19821
19819
|
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
19822
19820
|
import { createServer } from "http";
|
|
19823
19821
|
import * as http2 from "node:http2";
|
|
@@ -19874,8 +19872,8 @@ var init_listen = __esm({
|
|
|
19874
19872
|
const { port, host } = config;
|
|
19875
19873
|
if (port && typeof port !== "number") throw new Error("If specified, port must be a number");
|
|
19876
19874
|
const bindInfo = host ? `HTTPS ${host} ${port}` : `HTTPS ${port}`;
|
|
19877
|
-
|
|
19878
|
-
|
|
19875
|
+
ok5(config.key && existsSync4(config.key), "Key file not found");
|
|
19876
|
+
ok5(config.cert && existsSync4(config.cert), "Cert file not found");
|
|
19879
19877
|
const key = readFileSync3(config.key);
|
|
19880
19878
|
const cert = readFileSync3(config.cert);
|
|
19881
19879
|
super(http2.createSecureServer({ key, cert, allowHTTP1: true }), router, bindInfo);
|
|
@@ -20143,6 +20141,23 @@ var init_sqlite_adapter = __esm({
|
|
|
20143
20141
|
argTypes: []
|
|
20144
20142
|
}).then((e) => e.rows.map((e2) => e2[0]))
|
|
20145
20143
|
);
|
|
20144
|
+
if (applied_migrations.size && !applied_migrations.has("20250406213424_init")) {
|
|
20145
|
+
console.log("Unknown migrations have been found in the database.");
|
|
20146
|
+
console.log("There is no way to guarentee the integrity of the database under these conditions.");
|
|
20147
|
+
console.log("The only way I know of that you could be seeing this message is if you installed this verion of MWS in a project either from the PR branch or from a newer version of MWS.");
|
|
20148
|
+
console.log("Please revert back to the version you had installed before.");
|
|
20149
|
+
console.log("The sqlite database can be opened manually with any third-party SQLite tool to retrieve your data. If you do this, make sure you keep all the files in the store folder together, as they are all an integral part of your sqlite database and deleting any of them manually is very likely to cause data loss.");
|
|
20150
|
+
process.exit();
|
|
20151
|
+
} else {
|
|
20152
|
+
console.log([
|
|
20153
|
+
"=======================================================================================",
|
|
20154
|
+
"This version of MWS is no longer supported. It is an alpha version",
|
|
20155
|
+
"and you shouldn't be using it in production anyway. Please export any",
|
|
20156
|
+
"wikis you want to keep by opening them and downloading them as single-file",
|
|
20157
|
+
"wikis by clicking on the cloud status icon and then 'save snapshot for offline use'.",
|
|
20158
|
+
"======================================================================================="
|
|
20159
|
+
].join("\n"));
|
|
20160
|
+
}
|
|
20146
20161
|
const migrations = await readdir(dist_resolve("../prisma/migrations"));
|
|
20147
20162
|
migrations.sort();
|
|
20148
20163
|
const new_migrations = migrations.filter((m) => !applied_migrations.has(m) && m !== "migration_lock.toml");
|
|
@@ -20177,11 +20192,83 @@ var init_sqlite_adapter = __esm({
|
|
|
20177
20192
|
}
|
|
20178
20193
|
});
|
|
20179
20194
|
|
|
20195
|
+
// package.json
|
|
20196
|
+
var package_default;
|
|
20197
|
+
var init_package = __esm({
|
|
20198
|
+
"package.json"() {
|
|
20199
|
+
package_default = {
|
|
20200
|
+
name: "@tiddlywiki/mws",
|
|
20201
|
+
description: "TiddlyWiki Multiple Wiki Server",
|
|
20202
|
+
private: false,
|
|
20203
|
+
version: "0.0.21",
|
|
20204
|
+
type: "module",
|
|
20205
|
+
exports: {
|
|
20206
|
+
types: "./src/server.ts",
|
|
20207
|
+
require: "./dist/server.cjs",
|
|
20208
|
+
import: "./dist/server.js"
|
|
20209
|
+
},
|
|
20210
|
+
files: [
|
|
20211
|
+
"dist",
|
|
20212
|
+
"prisma",
|
|
20213
|
+
"react-user-mgmt",
|
|
20214
|
+
"tiddlywiki-types",
|
|
20215
|
+
"editions/mws-docs",
|
|
20216
|
+
"plugins/client",
|
|
20217
|
+
"scripts.mjs"
|
|
20218
|
+
],
|
|
20219
|
+
engines: {
|
|
20220
|
+
node: ">=18"
|
|
20221
|
+
},
|
|
20222
|
+
scripts: {
|
|
20223
|
+
docs: "node scripts.mjs docs",
|
|
20224
|
+
start: "node scripts.mjs start",
|
|
20225
|
+
certs: "(cd runtime-config && bash localhost_certs.sh)",
|
|
20226
|
+
tsc: "tsc -p tsconfig.json --skipLibCheck",
|
|
20227
|
+
build: "npm run tsc && tsup && node mws.dev.mjs --client-build",
|
|
20228
|
+
"install-android": `GYP_DEFINES="android_ndk_path=''" npm install`,
|
|
20229
|
+
postinstall: "node scripts.mjs prisma:generate",
|
|
20230
|
+
env: "env",
|
|
20231
|
+
"test:pack": "(git clean -dfx tests && npm pack --pack-destination tests && cd tests && npm install && npm install ./tiddlywiki-mws-$npm_package_version.tgz --no-save && npm test)",
|
|
20232
|
+
test: "(git clean -dfx tests && cd tests && npm install .. --no-save && npm test)",
|
|
20233
|
+
fulltest: "mv node_modules node_modules_old; npm run test:pack; mv node_modules_old node_modules",
|
|
20234
|
+
prepublishOnly: "npm run build"
|
|
20235
|
+
},
|
|
20236
|
+
dependencies: {
|
|
20237
|
+
"@prisma/adapter-better-sqlite3": "6.7.0-dev.48",
|
|
20238
|
+
"@prisma/client": "6.6.0",
|
|
20239
|
+
"@serenity-kit/opaque": "^0.8.4",
|
|
20240
|
+
"@types/mime-types": "^2.1.4",
|
|
20241
|
+
"@types/node": "^22.13.1",
|
|
20242
|
+
"@types/send": "^0.17.4",
|
|
20243
|
+
"@types/sjcl": "^1.0.34",
|
|
20244
|
+
"@types/tiddlywiki": "file:tiddlywiki-types",
|
|
20245
|
+
prisma: "6.6.0",
|
|
20246
|
+
"source-map-support": "^0.5.21",
|
|
20247
|
+
zod: "^3.24.2"
|
|
20248
|
+
},
|
|
20249
|
+
devDependencies: {
|
|
20250
|
+
commander: "^13.1.0",
|
|
20251
|
+
esbuild: "0.25.2",
|
|
20252
|
+
"mime-types": "^2.1.35",
|
|
20253
|
+
send: "^1.1.0",
|
|
20254
|
+
sjcl: "^1.0.8",
|
|
20255
|
+
tiddlywiki: "^5.1.22",
|
|
20256
|
+
tsup: "^8.4.0",
|
|
20257
|
+
typescript: "^5.7.3",
|
|
20258
|
+
uuid: "^11.1.0"
|
|
20259
|
+
},
|
|
20260
|
+
peerDependencies: {
|
|
20261
|
+
tiddlywiki: "^5.1.22"
|
|
20262
|
+
}
|
|
20263
|
+
};
|
|
20264
|
+
}
|
|
20265
|
+
});
|
|
20266
|
+
|
|
20180
20267
|
// src/commander.ts
|
|
20181
20268
|
import * as path5 from "node:path";
|
|
20182
20269
|
import { PrismaClient as PrismaClient2 } from "@prisma/client";
|
|
20183
20270
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
20184
|
-
import { ok as
|
|
20271
|
+
import { ok as ok6 } from "node:assert";
|
|
20185
20272
|
var _StartupCommander, StartupCommander, _Commander, Commander;
|
|
20186
20273
|
var init_commander = __esm({
|
|
20187
20274
|
"src/commander.ts"() {
|
|
@@ -20193,6 +20280,7 @@ var init_commander = __esm({
|
|
|
20193
20280
|
init_esm();
|
|
20194
20281
|
init_commands();
|
|
20195
20282
|
init_sqlite_adapter();
|
|
20283
|
+
init_package();
|
|
20196
20284
|
_StartupCommander = class _StartupCommander {
|
|
20197
20285
|
constructor(config, $tw2, PasswordService) {
|
|
20198
20286
|
this.config = config;
|
|
@@ -20202,8 +20290,8 @@ var init_commander = __esm({
|
|
|
20202
20290
|
this.setupRequired = true;
|
|
20203
20291
|
this.fieldModules = $tw2.Tiddler.fieldModules;
|
|
20204
20292
|
if (config.config?.pathPrefix) {
|
|
20205
|
-
|
|
20206
|
-
|
|
20293
|
+
ok6(config.config.pathPrefix.startsWith("/"), "pathPrefix must start with a slash");
|
|
20294
|
+
ok6(!config.config.pathPrefix.endsWith("/"), "pathPrefix must not end with a slash");
|
|
20207
20295
|
}
|
|
20208
20296
|
this.wikiPath = config.wikiPath;
|
|
20209
20297
|
this.storePath = path5.resolve(this.wikiPath, "store");
|
|
@@ -20226,12 +20314,17 @@ var init_commander = __esm({
|
|
|
20226
20314
|
contentTypeInfo: $tw2.config.contentTypeInfo,
|
|
20227
20315
|
storePath: this.storePath,
|
|
20228
20316
|
pathPrefix: config.config?.pathPrefix ?? "",
|
|
20229
|
-
saveLargeTextToFileSystem: void 0
|
|
20317
|
+
saveLargeTextToFileSystem: void 0,
|
|
20318
|
+
enablePluginCache: config.config?.enablePluginCache ?? false
|
|
20230
20319
|
};
|
|
20231
20320
|
this.SessionManager = config.SessionManager || SessionManager;
|
|
20232
20321
|
this.AttachmentService = config.AttachmentService || AttachmentService;
|
|
20233
20322
|
this.adapter = new SqliteAdapter(this.databasePath);
|
|
20234
20323
|
this.engine = new PrismaClient2({ log: ["info", "warn"], adapter: this.adapter.adapter });
|
|
20324
|
+
this.versions = {
|
|
20325
|
+
tiddlywiki: $tw2.packageInfo.version,
|
|
20326
|
+
mws: package_default.version
|
|
20327
|
+
};
|
|
20235
20328
|
}
|
|
20236
20329
|
async init() {
|
|
20237
20330
|
await this.adapter.init();
|
|
@@ -20239,6 +20332,19 @@ var init_commander = __esm({
|
|
|
20239
20332
|
const users = await this.engine.users.count();
|
|
20240
20333
|
if (!users) {
|
|
20241
20334
|
this.setupRequired = true;
|
|
20335
|
+
} else {
|
|
20336
|
+
const checkBags = await this.engine.bags.findMany({
|
|
20337
|
+
where: { bag_name: { startsWith: "$:/" } },
|
|
20338
|
+
select: { bag_name: true, tiddlers: { select: { title: true } } }
|
|
20339
|
+
});
|
|
20340
|
+
checkBags.forEach((e) => {
|
|
20341
|
+
e.tiddlers.forEach((f) => {
|
|
20342
|
+
if (f.title !== e.bag_name) {
|
|
20343
|
+
if (e.bag_name === "$:/plugins/tiddlywiki/codemirror-fullscreen-editing" && f.title === "$:/plugins/tiddlywiki/codemirror-fullscreen") return;
|
|
20344
|
+
console.log(`The bag ${e.bag_name} has a tiddler ${f.title}, in the future this will be unsupported. This can occur if the top bag in a recipe is one of the system bags (starting with $:/)`);
|
|
20345
|
+
}
|
|
20346
|
+
});
|
|
20347
|
+
});
|
|
20242
20348
|
}
|
|
20243
20349
|
}
|
|
20244
20350
|
$transaction(fn, options) {
|