@tiddlywiki/mws 0.0.16 → 0.0.17

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 +20 -33
  2. package/dist/server.d.ts +15 -9
  3. package/dist/server.js +114 -45
  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,22 @@
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
+ ## How to run
6
20
 
7
21
  The init command creates a new folder and installs what you need to get started.
8
22
 
@@ -36,42 +50,15 @@ If there are any database changes, MWS should pick them up and apply them. The c
36
50
  If you want to work on the project,
37
51
 
38
52
  - `git clone https://github.com/TiddlyWiki/MultiWikiServer`
53
+ - `cd MultiWikiServer`
39
54
  - `npm install` or `npm run install-android`
40
55
  - `cd react-user-mgmt`
41
56
  - `npm install`
42
57
  - `cd ..`
43
- - `npm run certs` - if you want https
58
+ - `npm run certs` - if you want https (unix only)
44
59
  - `npm start` - this runs the build and test every time, but it's pretty fast
45
60
 
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.
61
+ The development wiki will be active at http://localhost:8080/dev
76
62
 
63
+ You can change the listeners as explained in the mws.dev.mjs file.
77
64
 
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,7 +11660,7 @@ 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
11665
  user_id && role_ids?.length && {
11666
11666
  acl: {
@@ -12154,12 +12154,12 @@ var init_StateObject = __esm({
12154
12154
  init_streamer();
12155
12155
  init_utils2();
12156
12156
  _StateObject = class _StateObject extends StreamerState {
12157
- constructor(streamer, routePath, bodyFormat, user, commander2, tiddlerCache) {
12157
+ constructor(streamer, routePath, bodyFormat, user, router, tiddlerCache) {
12158
12158
  super(streamer);
12159
12159
  this.routePath = routePath;
12160
12160
  this.bodyFormat = bodyFormat;
12161
12161
  this.user = user;
12162
- this.commander = commander2;
12162
+ this.router = router;
12163
12163
  this.tiddlerCache = tiddlerCache;
12164
12164
  this.z = z3;
12165
12165
  this.STREAM_ENDED = STREAM_ENDED;
@@ -12170,9 +12170,9 @@ var init_StateObject = __esm({
12170
12170
  this.$transaction = /* @__PURE__ */ __name(async (fn) => {
12171
12171
  return await this.engine.$transaction((prisma) => fn(prisma));
12172
12172
  }, "$transaction");
12173
- this.engine = commander2.engine;
12174
- this.config = commander2.siteConfig;
12175
- this.PasswordService = commander2.PasswordService;
12173
+ this.engine = router.engine;
12174
+ this.config = router.siteConfig;
12175
+ this.PasswordService = router.PasswordService;
12176
12176
  this.readMultipartData = readMultipartData.bind(this);
12177
12177
  this.sendResponse = sendResponse.bind(void 0, this.config, this);
12178
12178
  this.pathParams = Object.fromEntries(routePath.map(
@@ -12222,7 +12222,7 @@ var init_StateObject = __esm({
12222
12222
  * - **308 Permanent Redirect:** The resource has permanently moved; the client should use the new URL in future requests.
12223
12223
  */
12224
12224
  redirect(location, pushLocation) {
12225
- return this.sendEmpty(302, { "Location": location });
12225
+ return this.sendEmpty(302, { "Location": this.config.pathPrefix + location });
12226
12226
  }
12227
12227
  sendSSE(retryMilliseconds) {
12228
12228
  if (retryMilliseconds !== void 0) {
@@ -12414,7 +12414,12 @@ var init_manager_recipes = __esm({
12414
12414
  }
12415
12415
  }
12416
12416
  },
12417
- where: isAdmin ? void 0 : { recipe_bags: { every: { bag: { OR } } } }
12417
+ where: isAdmin ? void 0 : {
12418
+ OR: [
12419
+ { recipe_bags: { every: { bag: { OR } } } },
12420
+ { owner_id: { equals: user_id, not: null } }
12421
+ ]
12422
+ }
12418
12423
  });
12419
12424
  const userListUser = !isAdmin && await prisma.users.findMany({
12420
12425
  select: { user_id: true, username: true }
@@ -12601,7 +12606,7 @@ var init_manager_recipes = __esm({
12601
12606
  const existing = await prisma.recipes.findUnique({ where: { recipe_name } });
12602
12607
  this.assertCreateOrUpdate({ user, type: "recipe", isCreate, owner_id, existing });
12603
12608
  const { isAdmin, user_id } = user;
12604
- const OR = this.checks.getWhereACL({ permission: "ADMIN", user_id });
12609
+ const OR = isAdmin ? void 0 : this.checks.getWhereACL({ permission: "ADMIN", user_id });
12605
12610
  const bags = new Map(
12606
12611
  await prisma.bags.findMany({
12607
12612
  where: { bag_name: { in: bag_names.map((e) => e.bag_name) } }
@@ -12609,14 +12614,12 @@ var init_manager_recipes = __esm({
12609
12614
  );
12610
12615
  const missing = bag_names.filter((e) => !bags.has(e.bag_name));
12611
12616
  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
- );
12617
+ const bagsAcl = new Map((await prisma.bags.findMany({
12618
+ where: { bag_name: { in: bag_names.map((e) => e.bag_name) }, OR }
12619
+ })).map((bag) => [bag.bag_name, bag]));
12617
12620
  const createBags = bag_names.map((bag, position) => ({
12618
12621
  bag_id: bags.get(bag.bag_name).bag_id,
12619
- with_acl: bagsAcl && bagsAcl.has(bag.bag_name) && bag.with_acl,
12622
+ with_acl: bagsAcl.has(bag.bag_name) && bag.with_acl,
12620
12623
  position
12621
12624
  }));
12622
12625
  if (existing) {
@@ -12914,13 +12917,20 @@ var init_TiddlerStore = __esm({
12914
12917
  init_esm_shims();
12915
12918
  init_utils2();
12916
12919
  _TiddlerStore = class _TiddlerStore {
12917
- constructor(commander2, prisma) {
12918
- this.commander = commander2;
12920
+ constructor(fieldModules, attachService, siteConfig, prisma) {
12921
+ this.fieldModules = fieldModules;
12922
+ this.attachService = attachService;
12923
+ this.siteConfig = siteConfig;
12919
12924
  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;
12925
+ this.storePath = this.siteConfig.storePath;
12926
+ }
12927
+ static fromCommander(commander2, prisma) {
12928
+ return new _TiddlerStore(
12929
+ commander2.$tw.Tiddler.fieldModules,
12930
+ new commander2.AttachmentService(commander2.siteConfig, prisma),
12931
+ commander2.siteConfig,
12932
+ prisma
12933
+ );
12924
12934
  }
12925
12935
  /*
12926
12936
  Returns null if a bag/recipe name is valid, or a string error message if not
@@ -13170,7 +13180,7 @@ var init_TiddlerStore = __esm({
13170
13180
  const stream = new Readable();
13171
13181
  stream._read = () => {
13172
13182
  const type = tiddlerInfo.tiddler.type || "text/plain";
13173
- stream.push(tiddlerInfo.tiddler.text || "", (this.config.contentTypeInfo[type] || { encoding: "utf8" }).encoding);
13183
+ stream.push(tiddlerInfo.tiddler.text || "", (this.siteConfig.contentTypeInfo[type] || { encoding: "utf8" }).encoding);
13174
13184
  stream.push(null);
13175
13185
  };
13176
13186
  return {
@@ -14093,7 +14103,13 @@ var init_bag_file_server = __esm({
14093
14103
  import_sjcl = __toESM(require_sjcl(), 1);
14094
14104
  _TiddlerServer = class _TiddlerServer extends TiddlerStore {
14095
14105
  constructor(state, prisma) {
14096
- super(state.commander, prisma);
14106
+ const router = state.router;
14107
+ super(
14108
+ router.fieldModules,
14109
+ new router.AttachmentService(router.siteConfig, prisma),
14110
+ router.siteConfig,
14111
+ prisma
14112
+ );
14097
14113
  this.state = state;
14098
14114
  }
14099
14115
  toTiddlyWebJson(info12) {
@@ -14611,6 +14627,62 @@ var init_router_tiddlers = __esm({
14611
14627
  }
14612
14628
  });
14613
14629
 
14630
+ // src/routes/tw-test.ts
14631
+ import { TiddlyWiki } from "tiddlywiki";
14632
+ function DocsRoute(parent, config) {
14633
+ const wikiRouter = new TWRouter(config);
14634
+ parent.defineRoute({
14635
+ method: ["OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE"],
14636
+ bodyFormat: "stream",
14637
+ path: /^\/mws(\/|$)/,
14638
+ pathParams: []
14639
+ }, async (state) => {
14640
+ if (state.urlInfo.pathname === "/mws") throw state.redirect("/mws/");
14641
+ const { req, res } = state.streamer;
14642
+ if (req.url === "/mws") req.url = "/mws/";
14643
+ wikiRouter.twserver.requestHandler(req, res, {});
14644
+ return new Promise((r, c) => res.on("finish", r).on("error", c));
14645
+ });
14646
+ }
14647
+ var _TWRouter, TWRouter;
14648
+ var init_tw_test = __esm({
14649
+ "src/routes/tw-test.ts"() {
14650
+ "use strict";
14651
+ init_esm_shims();
14652
+ __name(DocsRoute, "DocsRoute");
14653
+ _TWRouter = class _TWRouter {
14654
+ constructor(config) {
14655
+ this.config = config;
14656
+ this.$tw = TiddlyWiki();
14657
+ this.$tw.boot.argv = [
14658
+ "+plugins/tiddlywiki/tiddlyweb",
14659
+ "+plugins/tiddlywiki/filesystem",
14660
+ "./editions/mws-docs"
14661
+ ];
14662
+ this.$tw.preloadTiddler({
14663
+ text: "$protocol$//$host$" + this.config.pathPrefix + "/mws/",
14664
+ title: "$:/config/tiddlyweb/host"
14665
+ });
14666
+ this.$tw.boot.boot(() => {
14667
+ const Server2 = this.$tw.modules.execute("$:/core/modules/server/server.js", "tw-test.ts").Server;
14668
+ this.twserver = new Server2({
14669
+ wiki: this.$tw.wiki,
14670
+ variables: {
14671
+ // do not use a trailing slash
14672
+ "path-prefix": this.config.pathPrefix + "/mws",
14673
+ "root-tiddler": "$:/core/save/all"
14674
+ },
14675
+ routes: []
14676
+ });
14677
+ this.$tw.hooks.invokeHook("th-server-command-post-start", this.twserver, null, "mws");
14678
+ });
14679
+ }
14680
+ };
14681
+ __name(_TWRouter, "TWRouter");
14682
+ TWRouter = _TWRouter;
14683
+ }
14684
+ });
14685
+
14614
14686
  // import("./**/*/**/*") in src/routes/index.ts
14615
14687
  var globImport;
14616
14688
  var init_ = __esm({
@@ -14627,9 +14699,10 @@ var init_ = __esm({
14627
14699
  // src/routes/index.ts
14628
14700
  import { Prisma } from "@prisma/client";
14629
14701
  async function RootRoute(root, config) {
14702
+ if (process.env.ENABLE_DOCS_ROUTE) DocsRoute(root, config);
14630
14703
  TiddlerRouter.defineRoutes(root);
14631
14704
  ManagerRoutes(root, config);
14632
- if (typeof ENABLE_UNSAFE_PRISMA_ROUTE !== "undefined")
14705
+ if (process.env.ENABLE_UNSAFE_PRISMA_ROUTE)
14633
14706
  root.defineRoute({
14634
14707
  method: ["POST"],
14635
14708
  path: /^\/prisma$/,
@@ -14686,6 +14759,7 @@ var init_routes = __esm({
14686
14759
  init_utils2();
14687
14760
  init_managers();
14688
14761
  init_router_tiddlers();
14762
+ init_tw_test();
14689
14763
  init_();
14690
14764
  __name(RootRoute, "RootRoute");
14691
14765
  __name(importEsbuild, "importEsbuild");
@@ -14960,15 +15034,16 @@ var init_router = __esm({
14960
15034
  _Router = class _Router {
14961
15035
  constructor(rootRoute2, commander2, tiddlerCache) {
14962
15036
  this.rootRoute = rootRoute2;
14963
- this.commander = commander2;
14964
15037
  this.tiddlerCache = tiddlerCache;
14965
- this.pathPrefix = "";
14966
15038
  this.enableBrowserCache = true;
14967
15039
  this.enableGzip = false;
14968
15040
  this.csrfDisable = false;
14969
15041
  this.engine = commander2.engine;
14970
15042
  this.SessionManager = commander2.SessionManager;
14971
- this.pathPrefix = commander2.siteConfig.pathPrefix;
15043
+ this.siteConfig = commander2.siteConfig;
15044
+ this.PasswordService = commander2.PasswordService;
15045
+ this.fieldModules = commander2.$tw.Tiddler.fieldModules;
15046
+ this.AttachmentService = commander2.AttachmentService;
14972
15047
  }
14973
15048
  static async makeRouter(commander2, enableDevServer) {
14974
15049
  const sendDevServer = await setupDevServer(enableDevServer, commander2.siteConfig.pathPrefix);
@@ -14984,6 +15059,9 @@ var init_router = __esm({
14984
15059
  const cache = await startupCache(commander2);
14985
15060
  return new _Router(rootRoute2, commander2, cache);
14986
15061
  }
15062
+ get pathPrefix() {
15063
+ return this.siteConfig.pathPrefix;
15064
+ }
14987
15065
  handleIncomingRequest(req, res) {
14988
15066
  const [ok8, err, streamer] = function() {
14989
15067
  try {
@@ -15019,7 +15097,7 @@ var init_router = __esm({
15019
15097
  routePath,
15020
15098
  bodyFormat,
15021
15099
  authUser,
15022
- this.commander,
15100
+ this,
15023
15101
  this.tiddlerCache
15024
15102
  )
15025
15103
  );
@@ -15162,10 +15240,10 @@ var init_router = __esm({
15162
15240
  });
15163
15241
 
15164
15242
  // src/tiddlywiki.ts
15165
- import { TiddlyWiki } from "tiddlywiki";
15243
+ import { TiddlyWiki as TiddlyWiki2 } from "tiddlywiki";
15166
15244
  import { ok as ok5 } from "node:assert";
15167
15245
  async function bootTiddlyWiki(wikiPath) {
15168
- const $tw2 = TiddlyWiki();
15246
+ const $tw2 = TiddlyWiki2();
15169
15247
  $tw2.utils.eachAsync = eachAsync;
15170
15248
  $tw2.boot.boot = async (callback) => {
15171
15249
  $tw2.crypto = new $tw2.utils.Crypto();
@@ -15197,22 +15275,14 @@ async function bootTiddlyWiki(wikiPath) {
15197
15275
  startup: /* @__PURE__ */ __name(() => {
15198
15276
  }, "startup")
15199
15277
  });
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
15278
  $tw2.boot.argv = [
15211
15279
  "++" + dist_resolve("../plugins/client"),
15212
15280
  "+plugins/tiddlywiki/tiddlyweb",
15281
+ // these are sane defaults, these should be in the recipe
15213
15282
  "+themes/tiddlywiki/vanilla",
15214
15283
  "+themes/tiddlywiki/snowwhite",
15215
15284
  wikiPath
15285
+ // this should be inert
15216
15286
  ];
15217
15287
  await new Promise((resolve13) => $tw2.boot.boot(resolve13));
15218
15288
  ok5(!$tw2.boot.tasks.readBrowserTiddlers);
@@ -19004,7 +19074,7 @@ var init_load_plugin_bags = __esm({
19004
19074
  console.time(`${info.name} complete`);
19005
19075
  await loadPluginBags(
19006
19076
  this.$tw,
19007
- new TiddlerStore(this.commander, this.commander.engine),
19077
+ TiddlerStore.fromCommander(this.commander, this.commander.engine),
19008
19078
  this.commander.engine
19009
19079
  );
19010
19080
  console.timeEnd(`${info.name} complete`);
@@ -19160,7 +19230,7 @@ var init_load_wiki_folder = __esm({
19160
19230
  bagDescription: this.params[2],
19161
19231
  recipeName: this.params[3],
19162
19232
  recipeDescription: this.params[4],
19163
- store: new TiddlerStore(this.commander, this.commander.engine),
19233
+ store: TiddlerStore.fromCommander(this.commander, this.commander.engine),
19164
19234
  $tw: this.commander.$tw
19165
19235
  }));
19166
19236
  console.log(info3.name, "complete:", this.params[0]);
@@ -19213,7 +19283,7 @@ var init_save_archive = __esm({
19213
19283
  async execute() {
19214
19284
  if (this.params.length < 1) throw "Missing pathname";
19215
19285
  await this.commander.$transaction(async (prisma) => {
19216
- this.store = new TiddlerStore(this.commander, prisma);
19286
+ this.store = TiddlerStore.fromCommander(this.commander, prisma);
19217
19287
  await this.saveArchive();
19218
19288
  this.store = void 0;
19219
19289
  });
@@ -20012,6 +20082,7 @@ var init_commander = __esm({
20012
20082
  this.PasswordService = PasswordService;
20013
20083
  /** Signals that database setup is required. May be set to false by any qualified setup command. */
20014
20084
  this.setupRequired = true;
20085
+ this.fieldModules = $tw2.Tiddler.fieldModules;
20015
20086
  if (config.config?.pathPrefix) {
20016
20087
  ok7(config.config.pathPrefix.startsWith("/"), "pathPrefix must start with a slash");
20017
20088
  ok7(!config.config.pathPrefix.endsWith("/"), "pathPrefix must not end with a slash");
@@ -20067,7 +20138,6 @@ var init_commander = __esm({
20067
20138
  config.listeners = [];
20068
20139
  config.onListenersCreated = void 0;
20069
20140
  this.create_mws_listen = (params) => {
20070
- console.log(listeners);
20071
20141
  return new listen_exports.Command(params, this, listeners, onListenersCreated);
20072
20142
  };
20073
20143
  }
@@ -20168,7 +20238,6 @@ var init_commander = __esm({
20168
20238
  this.callback(params);
20169
20239
  return;
20170
20240
  }
20171
- console.log(params);
20172
20241
  new Promise(async (resolve13) => {
20173
20242
  const { Command: Command13, info: info12 } = command;
20174
20243
  try {