@tiddlywiki/mws 0.0.21 → 0.1.1
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 +42 -9
- package/dist/metafile-esm.json +1 -0
- package/dist/mws.d.ts +1 -0
- package/dist/mws.js +34710 -0
- package/dist/mws.js.map +1 -0
- package/editions/mws-docs/tiddlers/$__StoryList.tid +1 -2
- package/editions/mws-docs/tiddlers/2025-02-18 - User Management.md.meta +2 -2
- package/editions/mws-docs/tiddlers/2025-04-14 - client plugins /342/234/224/357/270/217.md" +31 -0
- package/editions/mws-docs/tiddlers/2025-04-14 - client plugins /342/234/224/357/270/217.md.meta" +5 -0
- package/editions/mws-docs/tiddlers/{2025-05-04.md → 2025-05-04 - event hooks /342/234/224/357/270/217.md" } +7 -1
- package/editions/mws-docs/tiddlers/2025-05-04 - event hooks /342/234/224/357/270/217.md.meta" +5 -0
- package/editions/mws-docs/tiddlers/{2025-05-06.md → 2025-05-06 - syncer design.md } +0 -4
- package/editions/mws-docs/tiddlers/2025-05-06 - syncer design.md.meta +5 -0
- package/editions/mws-docs/tiddlers/Allowed Methods.tid +8 -0
- package/editions/mws-docs/tiddlers/Application Access & Security.md +14 -84
- package/editions/mws-docs/tiddlers/Application Access & Security.md.meta +1 -1
- package/editions/mws-docs/tiddlers/Body Format.md +19 -0
- package/editions/mws-docs/tiddlers/Body Format.md.meta +4 -0
- package/editions/mws-docs/tiddlers/Checklists and Features.md +0 -3
- package/editions/mws-docs/tiddlers/Checklists and Features.md.meta +1 -1
- package/editions/mws-docs/tiddlers/Client Plugins.md +17 -0
- package/editions/mws-docs/tiddlers/Client Plugins.md.meta +5 -0
- package/editions/mws-docs/tiddlers/HTTP API.md +4 -3
- package/editions/mws-docs/tiddlers/HTTP API.md.meta +1 -1
- package/editions/mws-docs/tiddlers/HelloThere.tid +2 -2
- package/editions/mws-docs/tiddlers/Installation.tid +2 -3
- package/editions/mws-docs/tiddlers/MWS Architecture.tid +6 -1
- package/editions/mws-docs/tiddlers/Multi-user Trust.md +12 -0
- package/editions/mws-docs/tiddlers/Multi-user Trust.md.meta +5 -0
- package/editions/mws-docs/tiddlers/Notes.tid +5 -3
- package/editions/mws-docs/tiddlers/Oracle Attacks.md +29 -0
- package/editions/mws-docs/tiddlers/Oracle Attacks.md.meta +5 -0
- package/editions/mws-docs/tiddlers/Reference.md +1 -0
- package/editions/mws-docs/tiddlers/Reference.md.meta +1 -1
- package/editions/mws-docs/tiddlers/Request Handling.md +53 -0
- package/editions/mws-docs/tiddlers/Request Handling.md.meta +5 -0
- package/editions/mws-docs/tiddlers/Roadmap.tid +7 -0
- package/editions/mws-docs/tiddlers/Route Definers.md +206 -0
- package/editions/mws-docs/tiddlers/Route Definers.md.meta +5 -0
- package/editions/mws-docs/tiddlers/Security.md +75 -0
- package/editions/mws-docs/tiddlers/Security.md.meta +5 -0
- package/editions/mws-docs/tiddlers/Server Plugins.md +7 -0
- package/editions/mws-docs/tiddlers/Server Plugins.md.meta +5 -0
- package/editions/mws-docs/tiddlers/Startup Cache.md +28 -0
- package/editions/mws-docs/tiddlers/Startup Cache.md.meta +5 -0
- package/editions/mws-docs/tiddlers/TiddlerFields.md +5 -0
- package/editions/mws-docs/tiddlers/TiddlerFields.md.meta +4 -0
- package/editions/mws-docs/tiddlers/Usage.tid +2 -2
- package/editions/mws-docs/tiddlers/system/$__mws_cssvar-macro.tid +10 -0
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI +143 -0
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI.meta +7 -0
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI_styles.css +567 -0
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI_styles.css.meta +6 -0
- package/editions/mws-docs/tiddlers/system/$__theme.tid +6 -0
- package/editions/mws-docs/tiddlers/system/SplashScreen.tid +21 -0
- package/editions/mws-docs/tiddlers/system/StaticBanner.tid +1 -1
- package/editions/mws-docs/tiddlers/system/Styles.tid +13 -0
- package/editions/mws-docs/tiddlers/system/palette.tid +4 -1
- package/editions/mws-docs/tiddlers/system/sidebarlayout.tid +4 -1
- package/editions/mws-docs/tiddlers/x-mws-tiddler.md +35 -0
- package/editions/mws-docs/tiddlers/x-mws-tiddler.md.meta +5 -0
- package/mws.run.mjs +30 -0
- package/package.json +47 -36
- package/plugins/client/package-lock.json +46 -83
- package/plugins/client/package.json +1 -6
- package/plugins/client/plugin.info +2 -2
- package/plugins/client/src/fflate.d.ts +1501 -0
- package/plugins/client/src/multiwikiclientadaptor.ts +768 -317
- package/plugins/client/src/mws.d.ts +4 -0
- package/plugins/client/src/types.d.ts +3928 -0
- package/plugins/client/tiddlers/en-US.multids +3 -0
- package/plugins/client/tiddlers/status/SaveWikiButtonTemplate.tid +3 -0
- package/plugins/client/tiddlers/status/icon-cloud.tid +4 -0
- package/plugins/client/tiddlers/status/info-segment.tid +8 -0
- package/plugins/client/tiddlers/status/readonly-styles.tid +27 -0
- package/plugins/client/tiddlers/status/save-offline.tid +7 -0
- package/plugins/client/tiddlers/status/save-wiki-button.tid +26 -0
- package/plugins/client/tiddlers/status/styles.tid +44 -0
- package/plugins/client/tiddlers/status/syncer-actions-copy-logs.tid +6 -0
- package/plugins/client/tiddlers/status/syncer-actions-login-status.tid +9 -0
- package/plugins/client/tiddlers/status/syncer-actions-login.tid +8 -0
- package/plugins/client/tiddlers/status/syncer-actions-logout.tid +8 -0
- package/plugins/client/tiddlers/status/syncer-actions-refresh.tid +9 -0
- package/plugins/client/tiddlers/status/syncer-actions-save-snapshot.tid +9 -0
- package/plugins/client/tiddlers/status/tags-syncerdropdown.tid +2 -0
- package/plugins/client/tiddlers/syncer/multiwikiclientadaptor.js +640 -0
- package/plugins/client/tiddlers/syncer/syncer.js +831 -0
- package/plugins/client/tsconfig.json +19 -16
- package/plugins/filter-ast-to-sql/example-run-series.ts +146 -0
- package/plugins/server/SplashScreen.tid +150 -0
- package/plugins/server/fflate.js +9 -0
- package/plugins/server/plugin.info +8 -0
- package/plugins/server/readme +1 -0
- package/plugins/server/url-polyfill.js +294 -0
- package/prisma/client/client.d.ts +1 -0
- package/prisma/client/client.js +3 -0
- package/prisma/client/default.d.ts +1 -0
- package/prisma/client/default.js +3 -0
- package/prisma/client/edge.d.ts +1 -0
- package/prisma/client/edge.js +297 -0
- package/prisma/client/index-browser.js +275 -0
- package/prisma/client/index.d.ts +17770 -0
- package/prisma/client/index.js +324 -0
- package/prisma/client/package.json +173 -0
- package/prisma/client/query_engine_bg.js +811 -0
- package/prisma/client/query_engine_bg.wasm +0 -0
- package/prisma/client/runtime/edge-esm.js +10000 -0
- package/prisma/client/runtime/edge.js +10036 -0
- package/prisma/client/runtime/index-browser.d.ts +370 -0
- package/prisma/client/runtime/index-browser.js +2300 -0
- package/prisma/client/runtime/library.d.ts +3681 -0
- package/prisma/client/runtime/library.js +10604 -0
- package/prisma/client/runtime/react-native.js +10629 -0
- package/prisma/client/runtime/wasm-compiler-edge.js +11238 -0
- package/prisma/client/runtime/wasm-engine-edge.js +7801 -0
- package/prisma/client/schema.prisma +156 -0
- package/prisma/client/wasm-edge-light-loader.mjs +4 -0
- package/prisma/client/wasm-worker-loader.mjs +4 -0
- package/prisma/client/wasm.d.ts +1 -0
- package/prisma/client/wasm.js +304 -0
- package/prisma/global.ts +53 -0
- package/prisma/migrations/{20250406213424_init → 20250606001949_init}/migration.sql +46 -48
- package/prisma/migrations/20250613000526_remove_group/migration.sql +22 -0
- package/prisma/migrations/20250620014502_settings/migration.sql +5 -0
- package/prisma/migrations/20250707205229_recipes/migration.sql +20 -0
- package/prisma/migrations/migration_lock.toml +1 -1
- package/prisma/notes.md +1 -2
- package/prisma/schema.prisma +52 -49
- package/prisma/test.sqlite +0 -0
- package/prisma/test.sqlite-journal +0 -0
- package/public/react-admin/android-chrome-192x192.png +0 -0
- package/public/react-admin/android-chrome-512x512.png +0 -0
- package/public/react-admin/apple-touch-icon.png +0 -0
- package/{react-user-mgmt/public/esm-QKDFEP3B.js → public/react-admin/esm-L4WLVWJN.js} +1 -1
- package/public/react-admin/esm-L4WLVWJN.js.map +7 -0
- package/public/react-admin/esm-QMNGI3LF.js +3 -0
- package/public/react-admin/favicon-16x16.png +0 -0
- package/public/react-admin/favicon-32x32.png +0 -0
- package/public/react-admin/favicon.ico +0 -0
- package/public/react-admin/index.html +36 -0
- package/public/react-admin/main.js +268 -0
- package/public/react-admin/main.js.map +7 -0
- package/public/react-admin/site.webmanifest +20 -0
- package/public/react-admin/stats.json +1 -0
- package/scripts.mjs +134 -25
- package/dist/server.d.ts +0 -945
- package/dist/server.js +0 -20775
- package/dist/server.js.map +0 -1
- package/editions/mws-docs/tiddlers/2025-04-03 - Phases of Development.md +0 -64
- package/editions/mws-docs/tiddlers/2025-04-03 - Phases of Development.md.meta +0 -5
- package/editions/mws-docs/tiddlers/2025-04-13 - Plugins.md +0 -18
- package/editions/mws-docs/tiddlers/2025-04-13 - Plugins.md.meta +0 -5
- package/editions/mws-docs/tiddlers/2025-05-04.md.meta +0 -5
- package/editions/mws-docs/tiddlers/2025-05-06.md.meta +0 -5
- package/editions/mws-docs/tiddlers/Moduler Design.md +0 -29
- package/editions/mws-docs/tiddlers/Moduler Design.md.meta +0 -5
- package/editions/mws-docs/tiddlers/Request Server.md +0 -20
- package/editions/mws-docs/tiddlers/Request Server.md.meta +0 -5
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Dark +0 -173
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Dark.meta +0 -6
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Light +0 -172
- package/editions/mws-docs/tiddlers/system/$__palettes_MUI-Light.meta +0 -6
- package/plugins/client/tiddlers/config-tiddlers-filter.tid +0 -2
- package/plugins/client/tiddlers/config-use-server-sent-events.tid +0 -2
- package/plugins/client/tiddlers/info-segment.tid +0 -8
- package/plugins/client/tiddlers/multiwikiclientadaptor.js +0 -444
- package/prisma/migrations/20250407002306_disconnect_user_tables/migration.sql +0 -52
- package/prisma/old-db.sql +0 -144
- package/react-user-mgmt/README.md +0 -50
- package/react-user-mgmt/eslint.config.js +0 -28
- package/react-user-mgmt/package-lock.json +0 -2738
- package/react-user-mgmt/package.json +0 -31
- package/react-user-mgmt/public/index.html +0 -1
- package/react-user-mgmt/public/main.js +0 -295
- package/react-user-mgmt/public/main.js.map +0 -7
- package/react-user-mgmt/public/stats.json +0 -1
- package/react-user-mgmt/tsconfig.json +0 -40
- package/react-user-mgmt/types.d.ts +0 -2
- package/tiddlywiki-types/boot/boot.d.ts +0 -378
- package/tiddlywiki-types/boot/bootprefix.d.ts +0 -110
- package/tiddlywiki-types/core/modules/filters.d.ts +0 -107
- package/tiddlywiki-types/core/modules/tiddler.d.ts +0 -47
- package/tiddlywiki-types/core/modules/wiki-bulkops.d.ts +0 -36
- package/tiddlywiki-types/core/modules/wiki.d.ts +0 -208
- package/tiddlywiki-types/index.d.ts +0 -7
- package/tiddlywiki-types/package.json +0 -3
- /package/plugins/client/tiddlers/{sidebarsegment.tid → status/sidebarsegment.tid} +0 -0
- /package/plugins/client/tiddlers/{GettingStarted.tid → syncer/GettingStarted.tid} +0 -0
- /package/plugins/client/tiddlers/{config-incoming-updates-filter.tid → syncer/config-incoming-updates-filter.tid} +0 -0
- /package/plugins/client/tiddlers/{readme.tid → syncer/readme.tid} +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/chunk-YALWN5OT.js +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/chunk-YALWN5OT.js.map +0 -0
- /package/{react-user-mgmt/public/esm-QKDFEP3B.js.map → public/react-admin/esm-QMNGI3LF.js.map} +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/favicon.png +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/missing-favicon.png +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/motovun-jack.jpg +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/mws-icon.png +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/stats.css +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/stats.html +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/stats.js +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/stats.svg +0 -0
- /package/{react-user-mgmt/public → public/react-admin}/vite.svg +0 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
## Basic Route Definer
|
|
4
|
+
|
|
5
|
+
The original route definer which I used to type TiddlyWiki server routes, but the types ended up being extremely complicated for what I actually ended up needing. It is still used internally to actually define the routes, but the JavaScript side of it is very simple.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
`method`
|
|
9
|
+
: A subset of the [[Allowed Methods]].
|
|
10
|
+
|
|
11
|
+
`path`
|
|
12
|
+
: A regex starting with `^/` which matches the request. The first route handler which matches is used. Routes may be nested, and the full match is removed from the URL before matching children. If a parent route matches, it will be called, even if it has no child matches.
|
|
13
|
+
|
|
14
|
+
`denyFinal`
|
|
15
|
+
: If this route matches, but none of its children do, the server will return `404 NOT FOUND`. Otherwise, its state handler will be called and expected to handle the request, even if none of its children match.
|
|
16
|
+
|
|
17
|
+
`pathParams`
|
|
18
|
+
: An array of key names for regex match groups for the pathParams object.
|
|
19
|
+
|
|
20
|
+
`bodyFormat`
|
|
21
|
+
: The [[Body Format]] which the request wishes to receive. If the method is only GET and HEAD, this is ignored, as no request body is expected. Internally, the request is probably drained early, just in case a body was sent.
|
|
22
|
+
|
|
23
|
+
`handler` - a separate callback argument
|
|
24
|
+
: If the route matches, the handler is called. The handler is called at each level in order, so parents may add additional (out of type) properties to the state object or handle some requests and allow others to go through to the matched child.
|
|
25
|
+
|
|
26
|
+
### Match result
|
|
27
|
+
|
|
28
|
+
The StateObject has a routePath parameter containing the "path" through the "tree" of route definitions. In other words, it has the first matched route, and then the first matched child of that route, and then the first matched child of that route, and so on.
|
|
29
|
+
|
|
30
|
+
It is an array of objects with the following properties.
|
|
31
|
+
|
|
32
|
+
`route`
|
|
33
|
+
: an object containing the options for the route listed above
|
|
34
|
+
|
|
35
|
+
`params`
|
|
36
|
+
: an array of the match groups (`match.slice(1)`)
|
|
37
|
+
|
|
38
|
+
`remainingPath`
|
|
39
|
+
: The remaining URL to match (if this is zero-length, it will be a `/`)
|
|
40
|
+
|
|
41
|
+
## Zod Route Definers
|
|
42
|
+
|
|
43
|
+
The rest of the route definers are used by creating a class with the route definitions as properties, and then calling that class on server startup to register the routes.
|
|
44
|
+
|
|
45
|
+
<div style="color: rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: "Fira Code", "Cascadia Code", Consolas, "Courier New", monospace, Consolas, "Courier New", monospace; font-size: 15px; line-height: 20px; white-space: pre;"><div><span style="color: #569cd6;">class</span> <span style="color: #4ec9b0;">RoutesClass</span> { <span style="color: #9cdcfe;">test</span> = <span style="color: #dcdcaa;">zodManage</span>(<span style="color: #9cdcfe;">z</span> <span style="color: #569cd6;">=></span> <span style="color: #9cdcfe;">z</span>.<span style="color: #dcdcaa;">any</span>(), <span style="color: #569cd6;">async</span> <span style="color: #9cdcfe;">e</span> <span style="color: #569cd6;">=></span> <span style="color: #569cd6;">null</span>) }</div><div><span style="color: #569cd6;">const</span> <span style="color: #4fc1ff;">RoutesKeyMap</span>: <span style="color: #4ec9b0;">RouterKeyMap</span><<span style="color: #4ec9b0;">RoutesClass</span>, <span style="color: #4ec9b0;">true</span>> = { <span style="color: #9cdcfe;">test</span><span style="color: #9cdcfe;">:</span> <span style="color: #569cd6;">true</span> }</div><div><span style="color: #dcdcaa;">registerZodRoutes</span>(<span style="color: #4fc1ff;">root</span>, <span style="color: #569cd6;">new</span> <span style="color: #4ec9b0;">RoutesClass</span>(), <span style="color: #4ec9b0;">Object</span>.<span style="color: #dcdcaa;">keys</span>(<span style="color: #4fc1ff;">RoutesKeyMap</span>));</div></div>
|
|
46
|
+
|
|
47
|
+
`RoutesKeyMap` would have the keys of all the routes in the class, and the type makes sure no routes have been missed, while also allowing the class to have extra properties that are not routes.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## `zodRoute`
|
|
51
|
+
|
|
52
|
+
The `zodRoute` function creates type-safe route definitions with Zod validation. It takes a single configuration object with the following properties:
|
|
53
|
+
|
|
54
|
+
`method: string[]`
|
|
55
|
+
: An array of HTTP methods (e.g., `["GET", "POST"]`). Must be a subset of allowed methods.
|
|
56
|
+
|
|
57
|
+
`path: string`
|
|
58
|
+
: A slash-separated string path with optional parameters prefixed with `:` (e.g., `"/recipes/:recipe_name/tiddlers/:title"`).
|
|
59
|
+
|
|
60
|
+
`bodyFormat: BodyFormat`
|
|
61
|
+
: The expected body format: `"ignore"`, `"string"`, `"json"`, `"buffer"`, `"www-form-urlencoded"`, `"www-form-urlencoded-urlsearchparams"`, or `"stream"`. For GET and HEAD requests, this is always treated as `"ignore"`.
|
|
62
|
+
|
|
63
|
+
`zodPathParams: (z: Z2<"STRING">) => Record<string, ZodType>`
|
|
64
|
+
: A function that returns an object defining Zod validations for path parameters. The keys must match the parameter names in the path. If validation fails, returns 404.
|
|
65
|
+
|
|
66
|
+
`zodQueryParams?: (z: Z2<"STRING">) => Record<string, ZodType>`
|
|
67
|
+
: Optional function defining Zod validations for query parameters. Query params are arrays of strings by default.
|
|
68
|
+
|
|
69
|
+
`zodRequestBody?: (z: Z2<BodyFormat>) => ZodType`
|
|
70
|
+
: Optional function defining Zod validation for the request body. Only valid for `"string"`, `"json"`, and `"www-form-urlencoded"` body formats. If validation fails, returns 400.
|
|
71
|
+
|
|
72
|
+
`securityChecks?: { requestedWithHeader?: boolean }`
|
|
73
|
+
: Optional security checks. If `requestedWithHeader` is true, requires the `x-requested-with: fetch` header for non-GET/HEAD/OPTIONS requests.
|
|
74
|
+
|
|
75
|
+
`corsRequest?: (state: ZodState<"OPTIONS", "ignore", P, Q, ZodUndefined>) => Promise<symbol>`
|
|
76
|
+
: Optional CORS preflight handler for OPTIONS requests. Cannot authenticate but can provide endpoint information.
|
|
77
|
+
|
|
78
|
+
`inner: (state: ZodState<Method, BodyFormat, PathParams, QueryParams, RequestBody>) => Promise<JsonValue>`
|
|
79
|
+
: The main route handler that receives a fully validated and typed state object.
|
|
80
|
+
|
|
81
|
+
### Example
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
const getUser = zodRoute({
|
|
85
|
+
method: ["GET"],
|
|
86
|
+
path: "/users/:user_id",
|
|
87
|
+
bodyFormat: "ignore",
|
|
88
|
+
zodPathParams: z => ({
|
|
89
|
+
user_id: z.string().uuid()
|
|
90
|
+
}),
|
|
91
|
+
zodQueryParams: z => ({
|
|
92
|
+
include_roles: z.enum(["yes", "no"]).array().optional()
|
|
93
|
+
}),
|
|
94
|
+
inner: async (state) => {
|
|
95
|
+
const { user_id } = state.pathParams; // typed as { user_id: string }
|
|
96
|
+
const { include_roles } = state.queryParams; // typed as { include_roles?: ("yes"|"no")[] }
|
|
97
|
+
|
|
98
|
+
return await getUserById(user_id, include_roles?.[0] === "yes");
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## `admin` Helper Function
|
|
104
|
+
|
|
105
|
+
The `admin` function is a convenience wrapper around `zodRoute` specifically for admin API endpoints. It automatically sets up:
|
|
106
|
+
|
|
107
|
+
- Method: `["POST"]`
|
|
108
|
+
- Path: `"/admin/$key"` (where `$key` is replaced with the property name)
|
|
109
|
+
- Body format: `"json"`
|
|
110
|
+
- Security: Requires `x-requested-with: fetch` header
|
|
111
|
+
- Database transactions: Automatically wraps the handler in a Prisma transaction
|
|
112
|
+
- Authentication: Provides access to authenticated user state
|
|
113
|
+
|
|
114
|
+
### Signature
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
function admin<T extends ZodTypeAny, R extends JsonValue>(
|
|
118
|
+
zodRequest: (z: Z2<"JSON">) => T,
|
|
119
|
+
inner: (state: ZodState<"POST", "json", {}, {}, T>, prisma: PrismaTxnClient) => Promise<R>
|
|
120
|
+
): ZodRoute<"POST", "json", {}, {}, T, R>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Parameters
|
|
124
|
+
|
|
125
|
+
`zodRequest: (z: Z2<"JSON">) => ZodType`
|
|
126
|
+
: Function defining the expected shape of the JSON request body.
|
|
127
|
+
|
|
128
|
+
`inner: (state, prisma) => Promise<JsonValue>`
|
|
129
|
+
: Handler function that receives the validated state and a Prisma transaction client.
|
|
130
|
+
|
|
131
|
+
### Example
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
const user_create = admin(z => z.object({
|
|
135
|
+
username: z.string().min(3),
|
|
136
|
+
email: z.string().email(),
|
|
137
|
+
role_id: z.string().uuid()
|
|
138
|
+
}), async (state, prisma) => {
|
|
139
|
+
// state.data is typed based on the zodRequest schema
|
|
140
|
+
const { username, email, role_id } = state.data;
|
|
141
|
+
|
|
142
|
+
// Create user within the automatic transaction
|
|
143
|
+
const user = await prisma.users.create({
|
|
144
|
+
data: { username, email, role_id }
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return { user_id: user.user_id, username, email };
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## `registerZodRoutes` Function
|
|
152
|
+
|
|
153
|
+
This function registers multiple Zod routes from a class instance to a parent route. It's the bridge between route definitions and the actual router.
|
|
154
|
+
|
|
155
|
+
### Signature
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
function registerZodRoutes(
|
|
159
|
+
parent: ServerRoute,
|
|
160
|
+
router: object,
|
|
161
|
+
keys: string[]
|
|
162
|
+
): void
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Parameters
|
|
166
|
+
|
|
167
|
+
`parent: ServerRoute`
|
|
168
|
+
: The parent route to register child routes under.
|
|
169
|
+
|
|
170
|
+
`router: object`
|
|
171
|
+
: An instance of a class containing route definitions as properties.
|
|
172
|
+
|
|
173
|
+
`keys: string[]`
|
|
174
|
+
: Array of property names to register as routes. Usually `Object.keys(RouterKeyMap)`.
|
|
175
|
+
|
|
176
|
+
### Usage Pattern
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
export class UserManager {
|
|
180
|
+
static defineRoutes(root: ServerRoute) {
|
|
181
|
+
registerZodRoutes(root, new UserManager(), Object.keys(UserKeyMap));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
user_create = admin(z => z.object({
|
|
185
|
+
username: z.string(),
|
|
186
|
+
email: z.string().email()
|
|
187
|
+
}), async (state, prisma) => {
|
|
188
|
+
// Implementation
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
user_list = admin(z => z.undefined(), async (state, prisma) => {
|
|
192
|
+
// Implementation
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export const UserKeyMap: RouterKeyMap<UserManager, true> = {
|
|
197
|
+
user_create: true,
|
|
198
|
+
user_list: true,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Register during server startup
|
|
202
|
+
serverEvents.on("mws.routes", (root) => {
|
|
203
|
+
UserManager.defineRoutes(root);
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
## Use Cases
|
|
2
|
+
|
|
3
|
+
The primary use cases for MWS are
|
|
4
|
+
|
|
5
|
+
- Internal corporate information tools
|
|
6
|
+
- Public TiddlyWiki hosting
|
|
7
|
+
- Classroom scenarios
|
|
8
|
+
- Unrestricted trusted collaboration
|
|
9
|
+
|
|
10
|
+
## Still in Development
|
|
11
|
+
|
|
12
|
+
While these are the goals of MWS, it is still in early development, so none of these security restraints have actually been put in place yet. This is a work in progress, and the direction we're headed.
|
|
13
|
+
|
|
14
|
+
## Potential weaknesses
|
|
15
|
+
|
|
16
|
+
- **Not using HTTPS**
|
|
17
|
+
- **Oracles** that leak information through observable side-effects, such as network traffic, without directly revealing the contents.
|
|
18
|
+
- **Multi-user**: Users with write access can modify bags to gain access to other tiddlers in a different recipe that are supposed to be private.
|
|
19
|
+
|
|
20
|
+
## Don't add untrusted bags to your recipe.
|
|
21
|
+
|
|
22
|
+
Generally speaking, wiki text is quite powerful, almost as powerful as JavaScript. Button clicks can run widget actions, which can read and change any tiddler in the current recipe. Any user could easily cause TiddlyWiki to write tiddlers to other bags via actions.
|
|
23
|
+
|
|
24
|
+
A possible protection from this is using the referrer header to restrict edits to from a wiki to only follow the rules for the recipe that is open. In other words, if you have a recipe open, code in that page cannot use your credentials to somehow write to unrelated bags.
|
|
25
|
+
|
|
26
|
+
## Protection strategies
|
|
27
|
+
|
|
28
|
+
I just needed some place to list all the different things we could do to secure a site. Some of these would be optional depending on the level of mitigation required.
|
|
29
|
+
|
|
30
|
+
1. Obvious, "tie your shoes" security precautions.
|
|
31
|
+
2. Reasonable prevention of normal attack vectors.
|
|
32
|
+
3. Optional hardening to prevent targeted attacks.
|
|
33
|
+
4. Pedantic defenses against advanced attackers with full read-only access to the system or a backup.
|
|
34
|
+
|
|
35
|
+
### HTTPS
|
|
36
|
+
|
|
37
|
+
- Enable HTTPS site-wide using let's encrypt or another free certificate service.
|
|
38
|
+
|
|
39
|
+
### Cookies
|
|
40
|
+
|
|
41
|
+
[MDN - SetCookie](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie)
|
|
42
|
+
|
|
43
|
+
- Secure, HttpOnly, SameSite=strict, attributes
|
|
44
|
+
- Setting separate session cookies for the admin and wiki paths doesn't work because it's based on the request path, not the page path.
|
|
45
|
+
- Having a separate login subdomain and using oauth is a more complicated option.
|
|
46
|
+
|
|
47
|
+
### `no-cors`
|
|
48
|
+
|
|
49
|
+
- JavaScript `no-cors` mode allows `GET`, `HEAD`, and `POST` and sends relevant cookies.
|
|
50
|
+
- **`no-cors` prevents most headers, including custom headers, from being set.**
|
|
51
|
+
- Our standard `x-requested-with` header cannot be set.
|
|
52
|
+
|
|
53
|
+
### CORS headers
|
|
54
|
+
|
|
55
|
+
- Set CORS header to only allow expected origins. This doesn't prevent external CLI tools from accessing the site, only browser-based tools. This could also be set for certain endpoints or recipes to only allow specific bags to receive external requests.
|
|
56
|
+
|
|
57
|
+
### `Referer` header
|
|
58
|
+
|
|
59
|
+
- Only allow a wiki to access its own recipe endpoints for extra tight security. It is difficult to predict when a specific request might be malicious if tools are allowed to make requests from one recipe to another on a user's behalf. Specific referer headers could be white-listed as approved tool wikis.
|
|
60
|
+
- Don't allow wiki pages to access admin APIs.
|
|
61
|
+
|
|
62
|
+
### `Content-Security-Policy` header
|
|
63
|
+
|
|
64
|
+
- Able to block the page from making network requests, putting it in mostly read-only mode. This doesn't stop JavaScript from setting `location.href` nor prevent the user from clicking on `a href` links. This is more of a quick and dirty read-only, since it prevents ALL requests, including requests that might not actually change anything on the server.
|
|
65
|
+
|
|
66
|
+
### Other site configuration
|
|
67
|
+
|
|
68
|
+
- Don't allow admins to edit a wiki unless they are added to the ACL. The CSP header could be used to disable all network requests if the admin role does not have read permission, thus giving them true read-only access to the wiki.
|
|
69
|
+
- If compression is used, do not compress two bags in the same compression stream. This is a fairly extreme precaution with a limited likelihood.
|
|
70
|
+
|
|
71
|
+
## Other ideas
|
|
72
|
+
|
|
73
|
+
When you visit a page, you visit with page permissions only, and the page has to ask permission to use your full account permissions. The request could also be granular, where the page has to request the specific bag or wiki it wants access to.
|
|
74
|
+
|
|
75
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Server Plugins are a concept which has yet to be built but essentially involves subscribing to events and then taking (or not taking) a series of defined actions in response to the event.
|
|
2
|
+
|
|
3
|
+
For instance, on server startup, all plugins are asked to register any client plugins they may have. They would probably do this by adding that data to the tiddler cache, or returning the corresponding key-value pairs.
|
|
4
|
+
|
|
5
|
+
Server plugins should not expect the TiddlyWiki node instance to be available or pluggable. The TiddlyWiki instance is used to render plugins which are the equivalent of those available for single-file wikis. It is also used by the `load-wiki-folder` command, which uses the equivalent of `boot.js` code only.
|
|
6
|
+
|
|
7
|
+
The [[MWS routing system|Routing]] is directly borrowed from TiddlyWiki. Plugins may declare arbitrary routes (preferably namespaced) and the definitions try to be reasonably typed and have helpful utilities for handling incoming data.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
The startup cache records all the information about the TiddlyWiki plugin cache.
|
|
2
|
+
|
|
3
|
+
`pluginFiles`
|
|
4
|
+
: list of **key:** plugin title, **value:** cache-relative path to the plugin folder
|
|
5
|
+
|
|
6
|
+
`pluginHashes`
|
|
7
|
+
: list of **key:** plugin title, **value:** sha384 hash (prefixed with `sha384-`)
|
|
8
|
+
|
|
9
|
+
`filePlugins`
|
|
10
|
+
: `pluginFiles` with the key and value reversed. It should only be used for importing existing data folders which have path references to TiddlyWiki plugins (such as found in `tiddlywiki.info` files).
|
|
11
|
+
|
|
12
|
+
`requiredPlugins`
|
|
13
|
+
: the list of plugins that the site considers essential. Every wiki is always loaded with these plugins. Server Plugins can add or remove plugins from this list. It does not include the TiddlyWiki core, as that one is even more essential and can only be disabled with a setting on the recipe.
|
|
14
|
+
|
|
15
|
+
`cachePath`
|
|
16
|
+
: absolute path to the cache directory
|
|
17
|
+
|
|
18
|
+
## SHA-384 Hash options
|
|
19
|
+
|
|
20
|
+
`prefix`
|
|
21
|
+
: a Buffer to add to the hash before the plugin itself
|
|
22
|
+
|
|
23
|
+
`suffix`
|
|
24
|
+
: a Buffer to add to the hash after the plugin itself.
|
|
25
|
+
|
|
26
|
+
The hash is used for the integrity attribute when serving plugins externally. The exact file byte contents, along with `prefix` and `suffix`, must be hashed in the correct encoding. Presumably this is always UTF-8. When a plugin is served as an external JS file, it is wrapped with `prefix` and `suffix`.
|
|
27
|
+
|
|
28
|
+
The plugin itself is a [[tiddler fields object|TiddlerFields]] encoded with `JSON.stringify(tiddler.getFieldStrings())`.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
A tiddler fields object is the serializable tiddler exchange format. If in doubt, stringify. Tiddler field values are supposed to be strings but they aren't always. Tiddler field keys could contain the colon, which would break `.tid` files.
|
|
2
|
+
|
|
3
|
+
The definitive serializable form of a tiddler in TW5 is obtained by retrieving the tiddler from the wiki, and then calling `tiddler.getFieldString` on each field value. This is the form in which TiddlyWiki saves and loads single file wikis.
|
|
4
|
+
|
|
5
|
+
`.tid` files are not equivalent because they do not escape the colon character in field names. The official "workaround" is simply to save the tiddler as a JSON file.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
created: 20241105133737778
|
|
2
|
-
modified:
|
|
2
|
+
modified: 20250612223124466
|
|
3
3
|
tags: TableOfContents
|
|
4
4
|
title: Usage
|
|
5
5
|
type: text/vnd.tiddlywiki
|
|
@@ -11,4 +11,4 @@ On first start, a default user is created with username `admin` and password `12
|
|
|
11
11
|
If you intend to make an MWS installation available on the Internet, the server should first be secured with the following steps:
|
|
12
12
|
|
|
13
13
|
* Change the administrator password
|
|
14
|
-
* Install HTTPS server certificates.
|
|
14
|
+
* Install HTTPS server certificates from an online certificate service like [[Let's Encrypt|https://letsencrypt.org/]].
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
alert-background: <<cssvar alert-background>>
|
|
2
|
+
alert-border: <<cssvar alert-border>>
|
|
3
|
+
alert-highlight: <<cssvar alert-highlight>>
|
|
4
|
+
alert-muted-foreground: <<cssvar alert-muted-foreground>>
|
|
5
|
+
background: <<cssvar background>>
|
|
6
|
+
page-background: <<cssvar page-background>>
|
|
7
|
+
foreground: <<cssvar foreground>>
|
|
8
|
+
muted-foreground: <<cssvar muted-foreground>>
|
|
9
|
+
very-muted-foreground: <<cssvar very-muted-foreground>>
|
|
10
|
+
primary: <<cssvar primary>>
|
|
11
|
+
blockquote-bar: <<cssvar blockquote-bar>>
|
|
12
|
+
button-background: <<cssvar button-background>>
|
|
13
|
+
button-foreground: <<cssvar button-foreground>>
|
|
14
|
+
button-border: <<cssvar button-border>>
|
|
15
|
+
code-background: <<cssvar code-background>>
|
|
16
|
+
code-border: <<cssvar code-border>>
|
|
17
|
+
code-foreground: <<cssvar code-foreground>>
|
|
18
|
+
diff-delete-background: <<cssvar diff-delete-background>>
|
|
19
|
+
diff-delete-foreground: <<cssvar diff-delete-foreground>>
|
|
20
|
+
diff-equal-background: <<cssvar diff-equal-background>>
|
|
21
|
+
diff-equal-foreground: <<cssvar diff-equal-foreground>>
|
|
22
|
+
diff-insert-background: <<cssvar diff-insert-background>>
|
|
23
|
+
diff-insert-foreground: <<cssvar diff-insert-foreground>>
|
|
24
|
+
diff-invisible-background: <<cssvar diff-invisible-background>>
|
|
25
|
+
diff-invisible-foreground: <<cssvar diff-invisible-foreground>>
|
|
26
|
+
dragger-background: <<cssvar dragger-background>>
|
|
27
|
+
dragger-foreground: <<cssvar dragger-foreground>>
|
|
28
|
+
dirty-indicator: <<cssvar dirty-indicator>>
|
|
29
|
+
download-background: <<cssvar download-background>>
|
|
30
|
+
download-foreground: <<cssvar download-foreground>>
|
|
31
|
+
dropdown-background: <<cssvar dropdown-background>>
|
|
32
|
+
dropdown-border: <<cssvar dropdown-border>>
|
|
33
|
+
dropdown-tab-background-selected: <<cssvar dropdown-tab-background-selected>>
|
|
34
|
+
dropdown-tab-background: <<cssvar dropdown-tab-background>>
|
|
35
|
+
dropzone-background: <<cssvar dropzone-background>>
|
|
36
|
+
external-link-background: <<cssvar external-link-background>>
|
|
37
|
+
external-link-foreground: <<cssvar external-link-foreground>>
|
|
38
|
+
external-link-background-hover: <<cssvar external-link-background-hover>>
|
|
39
|
+
external-link-foreground-hover: <<cssvar external-link-foreground-hover>>
|
|
40
|
+
external-link-background-visited: <<cssvar external-link-background-visited>>
|
|
41
|
+
external-link-foreground-visited: <<cssvar external-link-foreground-visited>>
|
|
42
|
+
footnote-target-background: <<cssvar footnote-target-background>>
|
|
43
|
+
highlight-background: <<cssvar highlight-background>>
|
|
44
|
+
highlight-foreground: <<cssvar highlight-foreground>>
|
|
45
|
+
message-background: <<cssvar message-background>>
|
|
46
|
+
message-border: <<cssvar message-border>>
|
|
47
|
+
message-foreground: <<cssvar message-foreground>>
|
|
48
|
+
modal-backdrop: <<cssvar modal-backdrop>>
|
|
49
|
+
modal-background: <<cssvar modal-background>>
|
|
50
|
+
modal-border: <<cssvar modal-border>>
|
|
51
|
+
modal-footer-background: <<cssvar modal-footer-background>>
|
|
52
|
+
modal-footer-border: <<cssvar modal-footer-border>>
|
|
53
|
+
modal-header-border: <<cssvar modal-header-border>>
|
|
54
|
+
network-activity-foreground: <<cssvar network-activity-foreground>>
|
|
55
|
+
notification-background: <<cssvar notification-background>>
|
|
56
|
+
notification-border: <<cssvar notification-border>>
|
|
57
|
+
pre-background: <<cssvar pre-background>>
|
|
58
|
+
pre-border: <<cssvar pre-border>>
|
|
59
|
+
selection-background: <<cssvar selection-background>>
|
|
60
|
+
selection-foreground: <<cssvar selection-foreground>>
|
|
61
|
+
select-tag-background: <<cssvar select-tag-background>>
|
|
62
|
+
select-tag-foreground: <<cssvar select-tag-foreground>>
|
|
63
|
+
sidebar-button-foreground: <<cssvar sidebar-button-foreground>>
|
|
64
|
+
sidebar-controls-foreground-hover: <<cssvar sidebar-controls-foreground-hover>>
|
|
65
|
+
sidebar-controls-foreground: <<cssvar sidebar-controls-foreground>>
|
|
66
|
+
sidebar-foreground-shadow: <<cssvar sidebar-foreground-shadow>>
|
|
67
|
+
sidebar-foreground: <<cssvar sidebar-foreground>>
|
|
68
|
+
sidebar-muted-foreground-hover: <<cssvar sidebar-muted-foreground-hover>>
|
|
69
|
+
sidebar-muted-foreground: <<cssvar sidebar-muted-foreground>>
|
|
70
|
+
sidebar-tab-background-selected: <<cssvar sidebar-tab-background-selected>>
|
|
71
|
+
sidebar-tab-background: <<cssvar sidebar-tab-background>>
|
|
72
|
+
sidebar-tab-border-selected: <<cssvar sidebar-tab-border-selected>>
|
|
73
|
+
sidebar-tab-border: <<cssvar sidebar-tab-border>>
|
|
74
|
+
sidebar-tab-divider: <<cssvar sidebar-tab-divider>>
|
|
75
|
+
sidebar-tab-foreground-selected: <<cssvar sidebar-tab-foreground-selected>>
|
|
76
|
+
sidebar-tab-foreground: <<cssvar sidebar-tab-foreground>>
|
|
77
|
+
sidebar-tiddler-link-foreground-hover: <<cssvar sidebar-tiddler-link-foreground-hover>>
|
|
78
|
+
sidebar-tiddler-link-foreground: <<cssvar sidebar-tiddler-link-foreground>>
|
|
79
|
+
site-title-foreground: <<cssvar site-title-foreground>>
|
|
80
|
+
stability-stable: <<cssvar stability-stable>>
|
|
81
|
+
stability-experimental: <<cssvar stability-experimental>>
|
|
82
|
+
stability-deprecated: <<cssvar stability-deprecated>>
|
|
83
|
+
stability-legacy: <<cssvar stability-legacy>>
|
|
84
|
+
static-alert-foreground: <<cssvar static-alert-foreground>>
|
|
85
|
+
tab-background-selected: <<cssvar tab-background-selected>>
|
|
86
|
+
tab-background: <<cssvar tab-background>>
|
|
87
|
+
tab-border-selected: <<cssvar tab-border-selected>>
|
|
88
|
+
tab-border: <<cssvar tab-border>>
|
|
89
|
+
tab-divider: <<cssvar tab-divider>>
|
|
90
|
+
tab-foreground-selected: <<cssvar tab-foreground-selected>>
|
|
91
|
+
tab-foreground: <<cssvar tab-foreground>>
|
|
92
|
+
table-border: <<cssvar table-border>>
|
|
93
|
+
table-footer-background: <<cssvar table-footer-background>>
|
|
94
|
+
table-header-background: <<cssvar table-header-background>>
|
|
95
|
+
tag-background: <<cssvar tag-background>>
|
|
96
|
+
tag-foreground: <<cssvar tag-foreground>>
|
|
97
|
+
testcase-accent-level-1: <<cssvar testcase-accent-level-1>>
|
|
98
|
+
testcase-accent-level-2: <<cssvar testcase-accent-level-2>>
|
|
99
|
+
testcase-accent-level-3: <<cssvar testcase-accent-level-3>>
|
|
100
|
+
tiddler-background: <<cssvar tiddler-background>>
|
|
101
|
+
tiddler-border: <<cssvar tiddler-border>>
|
|
102
|
+
tiddler-controls-foreground-hover: <<cssvar tiddler-controls-foreground-hover>>
|
|
103
|
+
tiddler-controls-foreground-selected: <<cssvar tiddler-controls-foreground-selected>>
|
|
104
|
+
tiddler-controls-foreground: <<cssvar tiddler-controls-foreground>>
|
|
105
|
+
tiddler-editor-background: <<cssvar tiddler-editor-background>>
|
|
106
|
+
tiddler-editor-border-image: <<cssvar tiddler-editor-border-image>>
|
|
107
|
+
tiddler-editor-border: <<cssvar tiddler-editor-border>>
|
|
108
|
+
tiddler-editor-fields-even: <<cssvar tiddler-editor-fields-even>>
|
|
109
|
+
tiddler-editor-fields-odd: <<cssvar tiddler-editor-fields-odd>>
|
|
110
|
+
tiddler-info-background: <<cssvar tiddler-info-background>>
|
|
111
|
+
tiddler-info-border: <<cssvar tiddler-info-border>>
|
|
112
|
+
tiddler-info-tab-background: <<cssvar tiddler-info-tab-background>>
|
|
113
|
+
tiddler-link-background: <<cssvar tiddler-link-background>>
|
|
114
|
+
tiddler-link-foreground: <<cssvar tiddler-link-foreground>>
|
|
115
|
+
tiddler-subtitle-foreground: <<cssvar tiddler-subtitle-foreground>>
|
|
116
|
+
tiddler-title-foreground: <<cssvar tiddler-title-foreground>>
|
|
117
|
+
toolbar-new-button: <<cssvar toolbar-new-button>>
|
|
118
|
+
toolbar-options-button: <<cssvar toolbar-options-button>>
|
|
119
|
+
toolbar-save-button: <<cssvar toolbar-save-button>>
|
|
120
|
+
toolbar-info-button: <<cssvar toolbar-info-button>>
|
|
121
|
+
toolbar-edit-button: <<cssvar toolbar-edit-button>>
|
|
122
|
+
toolbar-close-button: <<cssvar toolbar-close-button>>
|
|
123
|
+
toolbar-delete-button: <<cssvar toolbar-delete-button>>
|
|
124
|
+
toolbar-cancel-button: <<cssvar toolbar-cancel-button>>
|
|
125
|
+
toolbar-done-button: <<cssvar toolbar-done-button>>
|
|
126
|
+
untagged-background: <<cssvar untagged-background>>
|
|
127
|
+
wikilist-background: <<cssvar wikilist-background>>
|
|
128
|
+
wikilist-item: <<cssvar wikilist-item>>
|
|
129
|
+
wikilist-info: <<cssvar wikilist-info>>
|
|
130
|
+
wikilist-title: <<cssvar wikilist-title>>
|
|
131
|
+
wikilist-title-svg: <<cssvar wikilist-title-svg>>
|
|
132
|
+
wikilist-url: <<cssvar wikilist-url>>
|
|
133
|
+
wikilist-button-open: <<cssvar wikilist-button-open>>
|
|
134
|
+
wikilist-button-open-hover: <<cssvar wikilist-button-open-hover>>
|
|
135
|
+
wikilist-button-reveal: <<cssvar wikilist-button-reveal>>
|
|
136
|
+
wikilist-button-reveal-hover: <<cssvar wikilist-button-reveal-hover>>
|
|
137
|
+
wikilist-button-remove: <<cssvar wikilist-button-remove>>
|
|
138
|
+
wikilist-button-remove-hover: <<cssvar wikilist-button-remove-hover>>
|
|
139
|
+
wikilist-toolbar-background: <<cssvar wikilist-toolbar-background>>
|
|
140
|
+
wikilist-toolbar-foreground: <<cssvar wikilist-toolbar-foreground>>
|
|
141
|
+
wikilist-droplink-dragover: <<cssvar wikilist-droplink-dragover>>
|
|
142
|
+
wikilist-button-background: <<cssvar wikilist-button-background>>
|
|
143
|
+
wikilist-button-foreground: <<cssvar wikilist-button-foreground>>
|