Skip to content

Commit 883f6cd

Browse files
committed
manually deduplicating code across actions
- basically I'm making everything on the server as consistent and repeatable as I possibly can across tabs. this is critical for bugs and maintenance. AI is good for designing a great idea, but its an absolute nightmare to maintain and update. So I'm going through manually and making everything as consistent and repeatable as I possibly can and locking everything down with Typescript types.
1 parent 0acd880 commit 883f6cd

18 files changed

Lines changed: 803 additions & 605 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ editions/mws-docs/tiddlers/$__StoryList.tid
1414
# Docker
1515
.env
1616
store/
17+
/test.sqlite

packages/admin-vanilla/src/app.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
RoleAdminRecord,
2525
UserAdminRecord,
2626
Reference,
27-
getSectionHeading
27+
getSectionHeading,
28+
TemplateTypes
2829
} from "./definition/tabs";
2930

3031
import { InMemoryAdminStorage } from "./definition/store";
@@ -432,7 +433,7 @@ function getLookupOptions(fieldKey: string, itemsByTab: AdminRecordStore): strin
432433
return Array.from(new Set(itemsByTab.plugins.map((item) => item.name).filter(Boolean)));
433434
}
434435
if (fieldKey === "userRoles") {
435-
return Array.from(new Set(itemsByTab.roles.map((item) => item.roleId).filter(Boolean)));
436+
return Array.from(new Set(itemsByTab.roles.map((item) => item.name).filter(Boolean)));
436437
}
437438
if (fieldKey === "permissions" || fieldKey === "recipePermissions") {
438439
return Array.from(new Set([
@@ -780,6 +781,21 @@ abstract class StringFieldTypeHandler extends FieldTypeHandler<string> {
780781

781782
}
782783

784+
class TemplateTypeFieldHandler extends FieldTypeHandler<TemplateTypes> {
785+
public initCreate() {
786+
return "simpleV1" as const;
787+
}
788+
789+
public renderEditor(ctx: FieldEditorContext<any>): JSX.Node {
790+
return null;
791+
}
792+
public renderSidebar(ctx: ReadonlyFieldContext): JSX.Node {
793+
return null;
794+
}
795+
796+
797+
}
798+
783799
class TextInputFieldHandler extends StringFieldTypeHandler {
784800
constructor(private readonly inputType: "text" | "number") {
785801
super();
@@ -1319,6 +1335,7 @@ const activityFeedFieldHandler = new ActivityFeedFieldHandler();
13191335
const metadataTableFieldHandler = new MetadataTableFieldHandler();
13201336
const tableFieldHandler = new TableFieldHandler();
13211337
const calloutFieldHandler = new CalloutFieldHandler();
1338+
const templateTypeFieldHandler = new TemplateTypeFieldHandler();
13221339

13231340
const fieldTypeHandlers = {
13241341
"string": textInputFieldHandler,
@@ -1339,6 +1356,7 @@ const fieldTypeHandlers = {
13391356
"table": tableFieldHandler,
13401357
"structured-preview": calloutFieldHandler,
13411358
"validation-report": calloutFieldHandler,
1359+
"template-type": templateTypeFieldHandler,
13421360
} satisfies Record<FieldType, FieldTypeHandler>;
13431361

13441362
const fallbackFieldHandler = new FallbackFieldHandler();

packages/admin-vanilla/src/definition/tabs.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { BagPermissionLevel, RecipePermissionLevel } from "@tiddlywiki/mws-prisma";
12

23
const halfWidth = "half" as const;
34
const fullWidth = "full" as const;
@@ -21,6 +22,7 @@ export type FieldSection = "authored" | "runtime" | "operations";
2122

2223

2324
export type FieldType =
25+
| "template-type"
2426
| "string"
2527
| "text"
2628
| "search"
@@ -205,6 +207,7 @@ const tabs = {
205207
{ key: "validationStatus", label: "Validation" },
206208
],
207209
fields: [
210+
{ key: "type", label: "Type", type: "template-type", section: "authored", mode: "create" },
208211
{ key: "name", label: "Name", type: "string", section: "authored", mode: "create edit" },
209212
{ key: "description", label: "Description", type: "text", section: "authored", mode: "create edit" },
210213
{ key: "lastUpdatedAt", label: "Updated", type: "string", section: "runtime", mode: "server" },
@@ -279,7 +282,7 @@ const tabs = {
279282
label: "Dependent wikis",
280283
type: "relationship-table",
281284
section: "runtime",
282-
mode: "server",
285+
mode: "",
283286
architecture: "Read-only relationship view listing all wikis that reference this template and will be revalidated or recompiled if it changes.",
284287
},
285288
{
@@ -432,19 +435,19 @@ const tabs = {
432435
eyebrow: "Access profiles",
433436
description: "Named access profiles that can be assigned to user accounts.",
434437
columns: [
435-
{ key: "roleId", label: "Role name" },
438+
{ key: "name", label: "Role name" },
436439
{ key: "description", label: "Role description" },
437440
],
438441
fields: [
439-
{ key: "roleId", label: "Role name", type: "string", section: "authored", mode: "create edit" },
442+
{ key: "name", label: "Role name", type: "string", section: "authored", mode: "create edit" },
440443
{ key: "description", label: "Role description", type: "text", section: "authored", mode: "create edit" },
441444
],
442445
fieldGroups: {
443446
authored: [
444-
{ title: "Role basics", keys: ["roleId", "description"], width: fullWidth, layout: stackLayout },
447+
{ title: "Role basics", keys: ["name", "description"], width: fullWidth, layout: stackLayout },
445448
],
446449
},
447-
sidebarDisplay: ["roleId", "description"],
450+
sidebarDisplay: ["name", "description"],
448451
},
449452
users: {
450453
id: "users",
@@ -505,26 +508,20 @@ export function getSectionHeading(section: FieldSection, mode: "create" | "edit"
505508
}
506509

507510

508-
type StoredTabKeys<T extends TabDefinition> = {
509-
[K in keyof TabDef]: MapFieldDefinitions<T["fields"]>[number] | "id"
510-
}[keyof TabDef];
511-
512-
type t1 = TabDef[TabId]["fields"][number]["key"]
513-
514511

515-
type StoredTabRecord<D extends TabDefinition, T> = Pick<T, StoredTabKeys<D> & keyof T>;
512+
type StoredTabRecord<D extends TabDefinition, T> = Pick<T, TabFieldKeys<D, "" | "create edit temp" | "create temp" | "edit temp"> & keyof T>;
513+
type SavedTabRecord<D extends TabDefinition, T> = Pick<T, TabFieldKeys<D, "" | "create edit temp" | "create temp" | "edit temp" | "server"> & keyof T>;
516514

515+
type TabFieldKeys<T extends TabDefinition, M extends Mode> = {
516+
[K in keyof TabDef]: MapFieldDefinitions<T["fields"], M>[number] | "id"
517+
}[keyof TabDef];
517518

518-
type MapFieldDefinitions<T> =
519-
T extends [infer F extends FieldDefinition, ...infer R] ? [FilterServerFields<F>, ...MapFieldDefinitions<R>] :
520-
T extends [infer F extends FieldDefinition] ? [FilterServerFields<F>] : [];
519+
type MapFieldDefinitions<T, M extends Mode> =
520+
T extends [infer F extends FieldDefinition, ...infer R] ? [FilterServerFields<F, M>, ...MapFieldDefinitions<R, M>] :
521+
T extends [infer F extends FieldDefinition] ? [FilterServerFields<F, M>] : [];
521522

522-
type FilterServerFields<T extends FieldDefinition> =
523-
T["mode"] extends "" ? never :
524-
T["mode"] extends "create edit temp" ? never :
525-
T["mode"] extends "create temp" ? never :
526-
T["mode"] extends "edit temp" ? never :
527-
T["key"];
523+
type FilterServerFields<T extends FieldDefinition, M extends Mode> =
524+
T["mode"] extends M ? never : T["key"];
528525

529526
export interface AdminRecordStore {
530527
wikis: WikiAdminRecord[];
@@ -548,6 +545,17 @@ export interface DataStore {
548545
availablePluginNames: Set<string>;
549546
};
550547

548+
export interface DataSave {
549+
wikis: SavedTabRecord<TabDef["wikis"], WikiAdminRecord>[];
550+
templates: SavedTabRecord<TabDef["templates"], TemplateAdminRecord>[];
551+
bags: SavedTabRecord<TabDef["bags"], BagAdminRecord>[];
552+
plugins: SavedTabRecord<TabDef["plugins"], PluginAdminRecord>[];
553+
roles: SavedTabRecord<TabDef["roles"], RoleAdminRecord>[];
554+
users: SavedTabRecord<TabDef["users"], UserAdminRecord>[];
555+
availableBagNames: Set<string>;
556+
availablePluginNames: Set<string>;
557+
};
558+
551559
export interface WikiAdminRecord {
552560
// server fields
553561
id: string;
@@ -558,7 +566,7 @@ export interface WikiAdminRecord {
558566
writablePrefixBags: readonly MappingRow[];
559567
readonlyBags: readonly string[];
560568
plugins: readonly string[];
561-
recipePermissions: readonly PermissionRow[];
569+
recipePermissions: readonly PermissionRow<RecipePermissionLevel>[];
562570
// client field
563571
templateName: string;
564572
defaultWritableBag: string;
@@ -576,15 +584,18 @@ export interface WikiAdminRecord {
576584
titleResolutionPreview: string;
577585
}
578586

587+
export type TemplateTypes = "simpleV1";
588+
579589
export interface TemplateAdminRecord {
580590
// server fields
581591
id: string;
592+
type: TemplateTypes;
582593
name: string;
583594
description: string;
584595
writablePrefixBags: readonly MappingRow[];
585596
readonlyBags: readonly string[];
586597
plugins: readonly string[];
587-
templatePermissions: readonly PermissionRow[];
598+
templatePermissions: readonly PermissionRow<RecipePermissionLevel>[];
588599
requiredPluginsEnabled: boolean;
589600
customHtmlEnabled: boolean;
590601
htmlContent: string;
@@ -604,7 +615,7 @@ export interface BagAdminRecord {
604615
id: string;
605616
name: string;
606617
description: string;
607-
permissions: readonly PermissionRow[];
618+
permissions: readonly PermissionRow<BagPermissionLevel>[];
608619
usedByCount: string;
609620
readonlyUsageCount: string;
610621
writableUsageCount: string;
@@ -629,7 +640,7 @@ export interface PluginAdminRecord {
629640

630641
export interface RoleAdminRecord {
631642
id: string;
632-
roleId: string;
643+
name: string;
633644
description: string;
634645
}
635646

packages/events/src/index.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { EventEmitter } from "events";
1313
export interface ServerEventsMap {
1414

1515
}
16-
16+
// the node implementation handles once in the once handler, not in emit
1717
export class ServerEvents extends EventEmitter<ServerEventsMap> {
1818
/** Use emitAsync instead */
1919
override emit!: never;
@@ -29,9 +29,18 @@ export class ServerEvents extends EventEmitter<ServerEventsMap> {
2929
eventName: keyof ServerEventsMap | K,
3030
...args: K extends keyof ServerEventsMap ? ServerEventsMap[K] : never
3131
) {
32-
// the node implementation handles once in the once handler, not in emit
3332
await Promise.all(this.listeners(eventName).map(e => e(...args)));
3433
}
34+
/** Call all listeners sync'ly, await them, catch errors per listener and forward them to emitLogCatcher. */
35+
async emitLog<K>(
36+
eventName: keyof ServerEventsMap | K,
37+
...args: K extends keyof ServerEventsMap ? ServerEventsMap[K] : never
38+
) {
39+
this.listeners(eventName).map(async e => { try { await e(...args) } catch (e) { this.emitLogCatcher?.(e); } })
40+
}
41+
42+
emitLogCatcher?: (error: unknown) => void;
43+
3544
}
3645

3746
/**

packages/mws/src/SendError.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { SendErrorItem } from "@tiddlywiki/server";
2+
import { TabId } from "./new-managers/wiki-actions";
23
declare module "@tiddlywiki/server" {
34
interface SendErrorReasonData {
45
"RECIPE_NOT_FOUND":
@@ -63,5 +64,8 @@ declare module "@tiddlywiki/server" {
6364

6465
"ARGUMENT_REQUIRED":
6566
SendErrorItem<400, { name: string }>;
67+
68+
"RECORD_KEY_NOT_FOUND":
69+
SendErrorItem<400, { table: TabId; name: string; }>
6670
}
6771
}

0 commit comments

Comments
 (0)