@tiddlywiki/mws 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +22 -33
  2. package/dist/server.d.ts +15 -9
  3. package/dist/server.js +119 -61
  4. package/dist/server.js.map +1 -1
  5. package/editions/mws-docs/tiddlers/$__StoryList.tid +1 -3
  6. package/editions/mws-docs/tiddlers/2025-02-18 - User Management.md +56 -0
  7. package/editions/mws-docs/tiddlers/2025-02-18 - User Management.md.meta +5 -0
  8. package/editions/mws-docs/tiddlers/2025-04-03 - Phases of Development.md +64 -0
  9. package/editions/mws-docs/tiddlers/2025-04-03 - Phases of Development.md.meta +5 -0
  10. package/editions/mws-docs/tiddlers/2025-04-13 - Plugins.md +18 -0
  11. package/editions/mws-docs/tiddlers/2025-04-13 - Plugins.md.meta +5 -0
  12. package/editions/mws-docs/tiddlers/2025-05-04.md +13 -0
  13. package/editions/mws-docs/tiddlers/2025-05-04.md.meta +5 -0
  14. package/editions/mws-docs/tiddlers/2025-05-06.md +19 -0
  15. package/editions/mws-docs/tiddlers/2025-05-06.md.meta +5 -0
  16. package/editions/mws-docs/tiddlers/Checklists and Features.md +15 -0
  17. package/editions/mws-docs/tiddlers/Checklists and Features.md.meta +5 -0
  18. package/editions/mws-docs/tiddlers/MWS Architecture.md +15 -0
  19. package/editions/mws-docs/tiddlers/MWS Architecture.md.meta +5 -0
  20. package/editions/mws-docs/tiddlers/Notes.tid +5 -0
  21. package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Dark +173 -0
  22. package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Dark.meta +6 -0
  23. package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Light +172 -0
  24. package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Light.meta +6 -0
  25. package/package.json +9 -21
  26. package/plugins/client/tiddlers/readme.tid +0 -3
  27. package/react-user-mgmt/public/main.js +32 -32
  28. package/react-user-mgmt/public/main.js.map +4 -4
  29. package/react-user-mgmt/public/stats.json +1 -1
  30. package/scripts.mjs +34 -0
  31. package/.env-cmdrc +0 -5
package/README.md CHANGED
@@ -1,8 +1,24 @@
1
1
  # MultiWikiServer
2
2
 
3
- MultiWikiServer for TiddlyWiki.
3
+ Multiple users, multiple wikis for TiddlyWiki.
4
4
 
5
- ### How to run
5
+ - Bag & Recipe system for storing tiddlers.
6
+ - User and Role management with ACL.
7
+ - Multiple database engines supported, using Prisma.
8
+ - Third-party OAuth and password-based login.
9
+
10
+ ## Flexible and Extendible
11
+
12
+ - Plugins can add routes and hooks.
13
+ - Abstractions everywhere, allowing flexibility.
14
+ - The source code is fully typed and easy to navigate.
15
+ - Admin endpoints can also be called from the CLI.
16
+
17
+ Most of these features are still in development.
18
+
19
+ **Do not use it to protect feelings or intellectual property.**
20
+
21
+ ## How to run
6
22
 
7
23
  The init command creates a new folder and installs what you need to get started.
8
24
 
@@ -36,42 +52,15 @@ If there are any database changes, MWS should pick them up and apply them. The c
36
52
  If you want to work on the project,
37
53
 
38
54
  - `git clone https://github.com/TiddlyWiki/MultiWikiServer`
55
+ - `cd MultiWikiServer`
39
56
  - `npm install` or `npm run install-android`
40
57
  - `cd react-user-mgmt`
41
58
  - `npm install`
42
59
  - `cd ..`
43
- - `npm run certs` - if you want https
60
+ - `npm run certs` - if you want https (unix only)
44
61
  - `npm start` - this runs the build and test every time, but it's pretty fast
45
62
 
46
- You can change the listeners as explained in the mws.dev.mjs file.
47
-
48
- ### The Server
49
-
50
- - Supports HTTP and HTTPS listeners. All requests are funneled into one stack.
51
- - Uses abstractions and state objects to make the server as flexible as possible.
52
- - Supports overriding classes to implement custom handling of various features.
53
- - All of the features from the old MWS branch have been converted to the new system.
54
-
55
- ### The Store
56
-
57
- - Written using Prisma and entirely promise-based.
58
- - Swapping to a different database engine should be a breeze. For sqlite it uses the libsql adapter, which also allows connections to libsql servers.
59
- - The tiddler text attachment system, which stores some tiddler bodies on the file system, is promise-based and can be modified to store files in the cloud.
60
-
61
- ### The Features
62
-
63
- - Bag & Recipe system for storing tiddlers.
64
- - User and Role management with ACL.
65
- - Attachment system for storing binary tiddlers on the file system.
66
- - Various import and export commands (currently still in development).
67
- - Customization of these features should be quite easy as they are fairly self-contained. The TypeScript code is fully typed and easy to navigate.
68
-
69
- ### Planned (hopefully) for the future
70
-
71
- - AuthJS or a similar integration that supports third-party OAuth (you can already write your own).
72
- - Compiling filters to SQL to optimize memory on both the client and server.
73
- - Support for other database and storage systems. Most likely MariaDB and Postgres.
74
- - Additional recipe strategies with features like prefixed bags and namespaces.
75
- - Server rendering of pages, for a more wikipedia-like experience.
63
+ The development wiki will be active at http://localhost:8080/dev
76
64
 
65
+ You can change the listeners as explained in the mws.dev.mjs file.
77
66
 
package/dist/server.d.ts CHANGED
@@ -5,13 +5,13 @@ import { PassThrough, Writable } from 'node:stream';
5
5
  import * as z from 'zod';
6
6
  import { z as z$1, ZodEffects } from 'zod';
7
7
  import { PrismaClient, Prisma } from '@prisma/client';
8
- import { ITXClientDenyList, Types } from '@prisma/client/runtime/library';
9
8
  import * as fs from 'fs';
10
9
  import * as tiddlywiki from 'tiddlywiki';
11
- import { TW } from 'tiddlywiki';
12
- import * as http2 from 'node:http2';
10
+ import { TW, TiddlerFieldModule } from 'tiddlywiki';
11
+ import { ITXClientDenyList, Types } from '@prisma/client/runtime/library';
13
12
  import * as commander from 'commander';
14
13
  import { SqlMigrationAwareDriverAdapterFactory, SqlDriverAdapter } from '@prisma/driver-adapter-utils';
14
+ import * as http2 from 'node:http2';
15
15
  import * as http2$1 from 'http2';
16
16
  import { OutgoingHttpHeaders, IncomingHttpHeaders as IncomingHttpHeaders$1, IncomingMessage as IncomingMessage$1, ServerResponse as ServerResponse$1 } from 'node:http';
17
17
 
@@ -227,6 +227,7 @@ declare class StartupCommander {
227
227
  config: MWSConfig;
228
228
  $tw: TW;
229
229
  PasswordService: PasswordService;
230
+ fieldModules: Record<string, TiddlerFieldModule>;
230
231
  constructor(config: MWSConfig, $tw: TW, PasswordService: PasswordService);
231
232
  init(): Promise<void>;
232
233
  wikiPath: string;
@@ -298,7 +299,7 @@ declare class StateObject<B extends BodyFormat = BodyFormat, M extends AllowedMe
298
299
  /** The bodyformat that ended up taking precedence. This should be correctly typed. */
299
300
  bodyFormat: B;
300
301
  user: AuthUser;
301
- commander: Commander;
302
+ router: Router;
302
303
  tiddlerCache: Router["tiddlerCache"];
303
304
  z: typeof z;
304
305
  STREAM_ENDED: typeof STREAM_ENDED;
@@ -336,7 +337,7 @@ declare class StateObject<B extends BodyFormat = BodyFormat, M extends AllowedMe
336
337
  /** The array of Route tree nodes the request matched. */
337
338
  routePath: RouteMatch[],
338
339
  /** The bodyformat that ended up taking precedence. This should be correctly typed. */
339
- bodyFormat: B, user: AuthUser, commander: Commander, tiddlerCache: Router["tiddlerCache"]);
340
+ bodyFormat: B, user: AuthUser, router: Router, tiddlerCache: Router["tiddlerCache"]);
340
341
  okUser(): void;
341
342
  okAdmin(): void;
342
343
  $transaction: <T>(fn: (prisma: PrismaTxnClient) => Promise<T>) => Promise<T>;
@@ -784,15 +785,18 @@ declare const BodyFormats: readonly ["stream", "string", "json", "buffer", "www-
784
785
  type BodyFormat = typeof BodyFormats[number];
785
786
  declare class Router {
786
787
  private rootRoute;
787
- private commander;
788
788
  private tiddlerCache;
789
789
  static makeRouter(commander: Commander, enableDevServer: boolean): Promise<Router>;
790
- pathPrefix: string;
791
790
  enableBrowserCache: boolean;
792
791
  enableGzip: boolean;
793
792
  csrfDisable: boolean;
793
+ siteConfig: SiteConfig;
794
+ get pathPrefix(): string;
794
795
  engine: Commander["engine"];
795
796
  private SessionManager;
797
+ PasswordService: Commander["PasswordService"];
798
+ fieldModules: Commander["$tw"]["Tiddler"]["fieldModules"];
799
+ AttachmentService: Commander["AttachmentService"];
796
800
  constructor(rootRoute: rootRoute$1, commander: Commander, tiddlerCache: CacheState);
797
801
  handleIncomingRequest(req: http.IncomingMessage | http2$1.Http2ServerRequest, res: http.ServerResponse | http2$1.Http2ServerResponse): void;
798
802
  handle(streamer: Streamer): Promise<void | typeof STREAM_ENDED>;
@@ -890,8 +894,10 @@ interface SiteConfig extends MWSConfigConfig {
890
894
  storePath: string;
891
895
  /**
892
896
  * The path prefix is a essentially folder mount point.
893
- * It starts with a slash, and ends without a slash.
894
- * If there is not a prefix, it is an empty string.
897
+ *
898
+ * It starts with a slash, and ends without a slash (`"/dev"`).
899
+ *
900
+ * If there is not a prefix, it is an empty string (`""`).
895
901
  */
896
902
  pathPrefix: string;
897
903
  }
package/dist/server.js CHANGED
@@ -11660,16 +11660,8 @@ var init_data_checks = __esm({
11660
11660
  // allow unowned for any user (conditional for anon reads)
11661
11661
  (user_id || allowAnon) && { acl: { none: {} }, owner_id: null },
11662
11662
  // allow owner for user
11663
- user_id && { owner_id: user_id },
11663
+ user_id && { owner_id: { equals: user_id, not: null } },
11664
11664
  // allow acl for user
11665
- user_id && role_ids?.length && {
11666
- acl: {
11667
- some: {
11668
- permission: { in: checkPerms },
11669
- role_id: { in: role_ids }
11670
- }
11671
- }
11672
- },
11673
11665
  user_id && role_ids?.length && {
11674
11666
  acl: {
11675
11667
  some: {
@@ -11857,9 +11849,6 @@ var init_streamer = __esm({
11857
11849
  this.isSecure = !!req.socket.encrypted;
11858
11850
  this.urlInfo = new URL(`https://${this.headers.host}${this.url}`);
11859
11851
  this.cookies = this.parseCookieString(req.headers.cookie || "");
11860
- if (this.cookies.getAll("session").length > 1) {
11861
- console.warn("Multiple session cookies found. This is very likely to cause problems. This may happen after changing the path prefix.");
11862
- }
11863
11852
  }
11864
11853
  // RIP Push Stream. it was a great idea.
11865
11854
  // async pushStream(path: string) {
@@ -12154,12 +12143,12 @@ var init_StateObject = __esm({
12154
12143
  init_streamer();
12155
12144
  init_utils2();
12156
12145
  _StateObject = class _StateObject extends StreamerState {
12157
- constructor(streamer, routePath, bodyFormat, user, commander2, tiddlerCache) {
12146
+ constructor(streamer, routePath, bodyFormat, user, router, tiddlerCache) {
12158
12147
  super(streamer);
12159
12148
  this.routePath = routePath;
12160
12149
  this.bodyFormat = bodyFormat;
12161
12150
  this.user = user;
12162
- this.commander = commander2;
12151
+ this.router = router;
12163
12152
  this.tiddlerCache = tiddlerCache;
12164
12153
  this.z = z3;
12165
12154
  this.STREAM_ENDED = STREAM_ENDED;
@@ -12170,9 +12159,9 @@ var init_StateObject = __esm({
12170
12159
  this.$transaction = /* @__PURE__ */ __name(async (fn) => {
12171
12160
  return await this.engine.$transaction((prisma) => fn(prisma));
12172
12161
  }, "$transaction");
12173
- this.engine = commander2.engine;
12174
- this.config = commander2.siteConfig;
12175
- this.PasswordService = commander2.PasswordService;
12162
+ this.engine = router.engine;
12163
+ this.config = router.siteConfig;
12164
+ this.PasswordService = router.PasswordService;
12176
12165
  this.readMultipartData = readMultipartData.bind(this);
12177
12166
  this.sendResponse = sendResponse.bind(void 0, this.config, this);
12178
12167
  this.pathParams = Object.fromEntries(routePath.map(
@@ -12222,7 +12211,7 @@ var init_StateObject = __esm({
12222
12211
  * - **308 Permanent Redirect:** The resource has permanently moved; the client should use the new URL in future requests.
12223
12212
  */
12224
12213
  redirect(location, pushLocation) {
12225
- return this.sendEmpty(302, { "Location": location });
12214
+ return this.sendEmpty(302, { "Location": this.config.pathPrefix + location });
12226
12215
  }
12227
12216
  sendSSE(retryMilliseconds) {
12228
12217
  if (retryMilliseconds !== void 0) {
@@ -12414,7 +12403,12 @@ var init_manager_recipes = __esm({
12414
12403
  }
12415
12404
  }
12416
12405
  },
12417
- where: isAdmin ? void 0 : { recipe_bags: { every: { bag: { OR } } } }
12406
+ where: isAdmin ? void 0 : {
12407
+ OR: [
12408
+ { recipe_bags: { every: { bag: { OR } } } },
12409
+ { owner_id: { equals: user_id, not: null } }
12410
+ ]
12411
+ }
12418
12412
  });
12419
12413
  const userListUser = !isAdmin && await prisma.users.findMany({
12420
12414
  select: { user_id: true, username: true }
@@ -12601,7 +12595,7 @@ var init_manager_recipes = __esm({
12601
12595
  const existing = await prisma.recipes.findUnique({ where: { recipe_name } });
12602
12596
  this.assertCreateOrUpdate({ user, type: "recipe", isCreate, owner_id, existing });
12603
12597
  const { isAdmin, user_id } = user;
12604
- const OR = this.checks.getWhereACL({ permission: "ADMIN", user_id });
12598
+ const OR = isAdmin ? void 0 : this.checks.getWhereACL({ permission: "ADMIN", user_id });
12605
12599
  const bags = new Map(
12606
12600
  await prisma.bags.findMany({
12607
12601
  where: { bag_name: { in: bag_names.map((e) => e.bag_name) } }
@@ -12609,14 +12603,12 @@ var init_manager_recipes = __esm({
12609
12603
  );
12610
12604
  const missing = bag_names.filter((e) => !bags.has(e.bag_name));
12611
12605
  if (missing.length) throw "Some bags not found: " + JSON.stringify(missing);
12612
- const bagsAcl = new Map(
12613
- await prisma.bags.findMany({
12614
- where: { bag_name: { in: bag_names.map((e) => e.bag_name) }, OR }
12615
- }).then((bags2) => bags2.map((bag) => [bag.bag_name, bag]))
12616
- );
12606
+ const bagsAcl = new Map((await prisma.bags.findMany({
12607
+ where: { bag_name: { in: bag_names.map((e) => e.bag_name) }, OR }
12608
+ })).map((bag) => [bag.bag_name, bag]));
12617
12609
  const createBags = bag_names.map((bag, position) => ({
12618
12610
  bag_id: bags.get(bag.bag_name).bag_id,
12619
- with_acl: bagsAcl && bagsAcl.has(bag.bag_name) && bag.with_acl,
12611
+ with_acl: bagsAcl.has(bag.bag_name) && bag.with_acl,
12620
12612
  position
12621
12613
  }));
12622
12614
  if (existing) {
@@ -12914,13 +12906,20 @@ var init_TiddlerStore = __esm({
12914
12906
  init_esm_shims();
12915
12907
  init_utils2();
12916
12908
  _TiddlerStore = class _TiddlerStore {
12917
- constructor(commander2, prisma) {
12918
- this.commander = commander2;
12909
+ constructor(fieldModules, attachService, siteConfig, prisma) {
12910
+ this.fieldModules = fieldModules;
12911
+ this.attachService = attachService;
12912
+ this.siteConfig = siteConfig;
12919
12913
  this.prisma = prisma;
12920
- this.fieldModules = this.commander.$tw.Tiddler.fieldModules;
12921
- this.attachService = new commander2.AttachmentService(commander2.siteConfig, prisma);
12922
- this.storePath = commander2.siteConfig.storePath;
12923
- this.config = commander2.siteConfig;
12914
+ this.storePath = this.siteConfig.storePath;
12915
+ }
12916
+ static fromCommander(commander2, prisma) {
12917
+ return new _TiddlerStore(
12918
+ commander2.$tw.Tiddler.fieldModules,
12919
+ new commander2.AttachmentService(commander2.siteConfig, prisma),
12920
+ commander2.siteConfig,
12921
+ prisma
12922
+ );
12924
12923
  }
12925
12924
  /*
12926
12925
  Returns null if a bag/recipe name is valid, or a string error message if not
@@ -13170,7 +13169,7 @@ var init_TiddlerStore = __esm({
13170
13169
  const stream = new Readable();
13171
13170
  stream._read = () => {
13172
13171
  const type = tiddlerInfo.tiddler.type || "text/plain";
13173
- stream.push(tiddlerInfo.tiddler.text || "", (this.config.contentTypeInfo[type] || { encoding: "utf8" }).encoding);
13172
+ stream.push(tiddlerInfo.tiddler.text || "", (this.siteConfig.contentTypeInfo[type] || { encoding: "utf8" }).encoding);
13174
13173
  stream.push(null);
13175
13174
  };
13176
13175
  return {
@@ -14093,7 +14092,13 @@ var init_bag_file_server = __esm({
14093
14092
  import_sjcl = __toESM(require_sjcl(), 1);
14094
14093
  _TiddlerServer = class _TiddlerServer extends TiddlerStore {
14095
14094
  constructor(state, prisma) {
14096
- super(state.commander, prisma);
14095
+ const router = state.router;
14096
+ super(
14097
+ router.fieldModules,
14098
+ new router.AttachmentService(router.siteConfig, prisma),
14099
+ router.siteConfig,
14100
+ prisma
14101
+ );
14097
14102
  this.state = state;
14098
14103
  }
14099
14104
  toTiddlyWebJson(info12) {
@@ -14611,6 +14616,62 @@ var init_router_tiddlers = __esm({
14611
14616
  }
14612
14617
  });
14613
14618
 
14619
+ // src/routes/tw-test.ts
14620
+ import { TiddlyWiki } from "tiddlywiki";
14621
+ function DocsRoute(parent, config) {
14622
+ const wikiRouter = new TWRouter(config);
14623
+ parent.defineRoute({
14624
+ method: ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE"],
14625
+ bodyFormat: "stream",
14626
+ path: /^\/mws(\/|$)/,
14627
+ pathParams: []
14628
+ }, async (state) => {
14629
+ if (state.urlInfo.pathname === "/mws") throw state.redirect("/mws/");
14630
+ const { req, res } = state.streamer;
14631
+ if (req.url === "/mws") req.url = "/mws/";
14632
+ wikiRouter.twserver.requestHandler(req, res, {});
14633
+ return new Promise((r, c) => res.on("finish", r).on("error", c));
14634
+ });
14635
+ }
14636
+ var _TWRouter, TWRouter;
14637
+ var init_tw_test = __esm({
14638
+ "src/routes/tw-test.ts"() {
14639
+ "use strict";
14640
+ init_esm_shims();
14641
+ __name(DocsRoute, "DocsRoute");
14642
+ _TWRouter = class _TWRouter {
14643
+ constructor(config) {
14644
+ this.config = config;
14645
+ this.$tw = TiddlyWiki();
14646
+ this.$tw.boot.argv = [
14647
+ "+plugins/tiddlywiki/tiddlyweb",
14648
+ "+plugins/tiddlywiki/filesystem",
14649
+ "./editions/mws-docs"
14650
+ ];
14651
+ this.$tw.preloadTiddler({
14652
+ text: "$protocol$//$host$" + this.config.pathPrefix + "/mws/",
14653
+ title: "$:/config/tiddlyweb/host"
14654
+ });
14655
+ this.$tw.boot.boot(() => {
14656
+ const Server2 = this.$tw.modules.execute("$:/core/modules/server/server.js", "tw-test.ts").Server;
14657
+ this.twserver = new Server2({
14658
+ wiki: this.$tw.wiki,
14659
+ variables: {
14660
+ // do not use a trailing slash
14661
+ "path-prefix": this.config.pathPrefix + "/mws",
14662
+ "root-tiddler": "$:/core/save/all"
14663
+ },
14664
+ routes: []
14665
+ });
14666
+ this.$tw.hooks.invokeHook("th-server-command-post-start", this.twserver, null, "mws");
14667
+ });
14668
+ }
14669
+ };
14670
+ __name(_TWRouter, "TWRouter");
14671
+ TWRouter = _TWRouter;
14672
+ }
14673
+ });
14674
+
14614
14675
  // import("./**/*/**/*") in src/routes/index.ts
14615
14676
  var globImport;
14616
14677
  var init_ = __esm({
@@ -14627,9 +14688,10 @@ var init_ = __esm({
14627
14688
  // src/routes/index.ts
14628
14689
  import { Prisma } from "@prisma/client";
14629
14690
  async function RootRoute(root, config) {
14691
+ if (process.env.ENABLE_DOCS_ROUTE) DocsRoute(root, config);
14630
14692
  TiddlerRouter.defineRoutes(root);
14631
14693
  ManagerRoutes(root, config);
14632
- if (typeof ENABLE_UNSAFE_PRISMA_ROUTE !== "undefined")
14694
+ if (process.env.ENABLE_UNSAFE_PRISMA_ROUTE)
14633
14695
  root.defineRoute({
14634
14696
  method: ["POST"],
14635
14697
  path: /^\/prisma$/,
@@ -14686,6 +14748,7 @@ var init_routes = __esm({
14686
14748
  init_utils2();
14687
14749
  init_managers();
14688
14750
  init_router_tiddlers();
14751
+ init_tw_test();
14689
14752
  init_();
14690
14753
  __name(RootRoute, "RootRoute");
14691
14754
  __name(importEsbuild, "importEsbuild");
@@ -14960,15 +15023,16 @@ var init_router = __esm({
14960
15023
  _Router = class _Router {
14961
15024
  constructor(rootRoute2, commander2, tiddlerCache) {
14962
15025
  this.rootRoute = rootRoute2;
14963
- this.commander = commander2;
14964
15026
  this.tiddlerCache = tiddlerCache;
14965
- this.pathPrefix = "";
14966
15027
  this.enableBrowserCache = true;
14967
15028
  this.enableGzip = false;
14968
15029
  this.csrfDisable = false;
14969
15030
  this.engine = commander2.engine;
14970
15031
  this.SessionManager = commander2.SessionManager;
14971
- this.pathPrefix = commander2.siteConfig.pathPrefix;
15032
+ this.siteConfig = commander2.siteConfig;
15033
+ this.PasswordService = commander2.PasswordService;
15034
+ this.fieldModules = commander2.$tw.Tiddler.fieldModules;
15035
+ this.AttachmentService = commander2.AttachmentService;
14972
15036
  }
14973
15037
  static async makeRouter(commander2, enableDevServer) {
14974
15038
  const sendDevServer = await setupDevServer(enableDevServer, commander2.siteConfig.pathPrefix);
@@ -14984,6 +15048,9 @@ var init_router = __esm({
14984
15048
  const cache = await startupCache(commander2);
14985
15049
  return new _Router(rootRoute2, commander2, cache);
14986
15050
  }
15051
+ get pathPrefix() {
15052
+ return this.siteConfig.pathPrefix;
15053
+ }
14987
15054
  handleIncomingRequest(req, res) {
14988
15055
  const [ok8, err, streamer] = function() {
14989
15056
  try {
@@ -15019,7 +15086,7 @@ var init_router = __esm({
15019
15086
  routePath,
15020
15087
  bodyFormat,
15021
15088
  authUser,
15022
- this.commander,
15089
+ this,
15023
15090
  this.tiddlerCache
15024
15091
  )
15025
15092
  );
@@ -15162,10 +15229,10 @@ var init_router = __esm({
15162
15229
  });
15163
15230
 
15164
15231
  // src/tiddlywiki.ts
15165
- import { TiddlyWiki } from "tiddlywiki";
15232
+ import { TiddlyWiki as TiddlyWiki2 } from "tiddlywiki";
15166
15233
  import { ok as ok5 } from "node:assert";
15167
15234
  async function bootTiddlyWiki(wikiPath) {
15168
- const $tw2 = TiddlyWiki();
15235
+ const $tw2 = TiddlyWiki2();
15169
15236
  $tw2.utils.eachAsync = eachAsync;
15170
15237
  $tw2.boot.boot = async (callback) => {
15171
15238
  $tw2.crypto = new $tw2.utils.Crypto();
@@ -15197,22 +15264,14 @@ async function bootTiddlyWiki(wikiPath) {
15197
15264
  startup: /* @__PURE__ */ __name(() => {
15198
15265
  }, "startup")
15199
15266
  });
15200
- $tw2.preloadTiddler({ title: "$:/plugins/tiddlywiki/multiwikiserver/startup.js" });
15201
- $tw2.modules.define("$:/plugins/tiddlywiki/multiwikiserver/startup.js", "startup", {
15202
- name: "multiwikiserver",
15203
- platforms: ["node"],
15204
- after: ["load-modules"],
15205
- before: ["story", "commands"],
15206
- synchronous: true,
15207
- startup: /* @__PURE__ */ __name(() => {
15208
- }, "startup")
15209
- });
15210
15267
  $tw2.boot.argv = [
15211
15268
  "++" + dist_resolve("../plugins/client"),
15212
15269
  "+plugins/tiddlywiki/tiddlyweb",
15270
+ // these are sane defaults, these should be in the recipe
15213
15271
  "+themes/tiddlywiki/vanilla",
15214
15272
  "+themes/tiddlywiki/snowwhite",
15215
15273
  wikiPath
15274
+ // this should be inert
15216
15275
  ];
15217
15276
  await new Promise((resolve13) => $tw2.boot.boot(resolve13));
15218
15277
  ok5(!$tw2.boot.tasks.readBrowserTiddlers);
@@ -15342,17 +15401,17 @@ var init_sessions = __esm({
15342
15401
  registerZodRoutes(root, new _SessionManager(), Object.keys(SessionKeyMap));
15343
15402
  }
15344
15403
  static async parseIncomingRequest(streamer, router) {
15345
- const sessionId = streamer.cookies.get("session");
15346
- const session = sessionId && await router.engine.sessions.findUnique({
15347
- where: { session_id: sessionId },
15348
- select: { user: { select: { user_id: true, username: true, roles: { select: { role_id: true } } } } }
15404
+ const sessionId = streamer.cookies.getAll("session");
15405
+ const session = sessionId && await router.engine.sessions.findFirst({
15406
+ where: { session_id: { in: sessionId } },
15407
+ select: { session_id: true, user: { select: { user_id: true, username: true, roles: { select: { role_id: true } } } } }
15349
15408
  });
15350
15409
  if (sessionId && session) return {
15351
15410
  user_id: session.user.user_id,
15352
15411
  username: session.user.username,
15353
15412
  isAdmin: session.user.roles.some((e) => e.role_id === 1),
15354
15413
  role_ids: session.user.roles.map((e) => e.role_id),
15355
- sessionId,
15414
+ sessionId: session.session_id,
15356
15415
  isLoggedIn: true
15357
15416
  };
15358
15417
  else return {
@@ -19004,7 +19063,7 @@ var init_load_plugin_bags = __esm({
19004
19063
  console.time(`${info.name} complete`);
19005
19064
  await loadPluginBags(
19006
19065
  this.$tw,
19007
- new TiddlerStore(this.commander, this.commander.engine),
19066
+ TiddlerStore.fromCommander(this.commander, this.commander.engine),
19008
19067
  this.commander.engine
19009
19068
  );
19010
19069
  console.timeEnd(`${info.name} complete`);
@@ -19160,7 +19219,7 @@ var init_load_wiki_folder = __esm({
19160
19219
  bagDescription: this.params[2],
19161
19220
  recipeName: this.params[3],
19162
19221
  recipeDescription: this.params[4],
19163
- store: new TiddlerStore(this.commander, this.commander.engine),
19222
+ store: TiddlerStore.fromCommander(this.commander, this.commander.engine),
19164
19223
  $tw: this.commander.$tw
19165
19224
  }));
19166
19225
  console.log(info3.name, "complete:", this.params[0]);
@@ -19213,7 +19272,7 @@ var init_save_archive = __esm({
19213
19272
  async execute() {
19214
19273
  if (this.params.length < 1) throw "Missing pathname";
19215
19274
  await this.commander.$transaction(async (prisma) => {
19216
- this.store = new TiddlerStore(this.commander, prisma);
19275
+ this.store = TiddlerStore.fromCommander(this.commander, prisma);
19217
19276
  await this.saveArchive();
19218
19277
  this.store = void 0;
19219
19278
  });
@@ -20012,6 +20071,7 @@ var init_commander = __esm({
20012
20071
  this.PasswordService = PasswordService;
20013
20072
  /** Signals that database setup is required. May be set to false by any qualified setup command. */
20014
20073
  this.setupRequired = true;
20074
+ this.fieldModules = $tw2.Tiddler.fieldModules;
20015
20075
  if (config.config?.pathPrefix) {
20016
20076
  ok7(config.config.pathPrefix.startsWith("/"), "pathPrefix must start with a slash");
20017
20077
  ok7(!config.config.pathPrefix.endsWith("/"), "pathPrefix must not end with a slash");
@@ -20067,7 +20127,6 @@ var init_commander = __esm({
20067
20127
  config.listeners = [];
20068
20128
  config.onListenersCreated = void 0;
20069
20129
  this.create_mws_listen = (params) => {
20070
- console.log(listeners);
20071
20130
  return new listen_exports.Command(params, this, listeners, onListenersCreated);
20072
20131
  };
20073
20132
  }
@@ -20168,7 +20227,6 @@ var init_commander = __esm({
20168
20227
  this.callback(params);
20169
20228
  return;
20170
20229
  }
20171
- console.log(params);
20172
20230
  new Promise(async (resolve13) => {
20173
20231
  const { Command: Command13, info: info12 } = command;
20174
20232
  try {