ocean-brain 0.6.1 → 0.6.3
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/package.json +1 -1
- package/server/client/dist/assets/{Calendar-BJHLksEZ.js → Calendar-DbPut8EU.js} +1 -1
- package/server/client/dist/assets/{Callout-wFx9oCas.js → Callout-DfX-gIUB.js} +1 -1
- package/server/client/dist/assets/{Graph-9piERaNR.js → Graph-yHE9m5fy.js} +1 -1
- package/server/client/dist/assets/{Image.es-Be7par_j.js → Image.es-CtMc7BW4.js} +1 -1
- package/server/client/dist/assets/{Note-CC9ybxXL.js → Note-BECn-AoI.js} +2 -2
- package/server/client/dist/assets/{Plus.es-BKm7uDYu.js → Plus.es-BGVIIQtM.js} +1 -1
- package/server/client/dist/assets/{Reminders-DRxA8v98.js → Reminders-CvEJf0eQ.js} +1 -1
- package/server/client/dist/assets/{Search-BunSQrqv.js → Search-CKZhztuV.js} +1 -1
- package/server/client/dist/assets/{SurfaceCard-FhlC8VSR.js → SurfaceCard-Bnfo5EOl.js} +1 -1
- package/server/client/dist/assets/{Tag-z_crJ5JP.js → Tag-C4oAwjs0.js} +1 -1
- package/server/client/dist/assets/{TagNotes-D7wIJ4jy.js → TagNotes-Rl1ZSJP6.js} +1 -1
- package/server/client/dist/assets/{Trash.es-ByMVwx0d.js → Trash.es-BcwUz2yM.js} +1 -1
- package/server/client/dist/assets/ViewNotes-B9Yc0A87.js +1 -0
- package/server/client/dist/assets/ViewSectionTableRenderer-C5yJBO7C.js +102 -0
- package/server/client/dist/assets/Views-Bqq0pUEE.js +1 -0
- package/server/client/dist/assets/{image.api-B5Qi-Txv.js → image.api-CuGTgzyQ.js} +1 -1
- package/server/client/dist/assets/{index-nYj7BL3a.js → index-B43ulIPo.js} +3 -3
- package/server/client/dist/assets/{index-cHJQCo8V.js → index-BQDR6hIA.js} +1 -1
- package/server/client/dist/assets/{index-Cd81UxEh.css → index-CDjYEIVf.css} +1 -1
- package/server/client/dist/assets/{manage-image-glbtBibn.js → manage-image-Bs4Kmevx.js} +1 -1
- package/server/client/dist/assets/{manage-image-detail-DcZggB1M.js → manage-image-detail-WARala-v.js} +1 -1
- package/server/client/dist/assets/{mcp-BBUpQdS9.js → mcp-C0Z3vKBa.js} +1 -1
- package/server/client/dist/assets/{placeholder-B_qb_qkc.js → placeholder-D080guzn.js} +3 -3
- package/server/client/dist/assets/{properties-CiyQv6Tb.js → properties-DbnmfbT5.js} +1 -1
- package/server/client/dist/assets/trash-D5ZiZfTk.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-CSKliBcc.js → useReminderMutate-h7d_S_X2.js} +2 -2
- package/server/client/dist/index.html +2 -2
- package/server/dist/features/view/graphql/view.type-defs.js +22 -0
- package/server/dist/features/view/graphql/view.type-defs.js.map +1 -1
- package/server/dist/features/view/services/workspace.js +77 -16
- package/server/dist/features/view/services/workspace.js.map +1 -1
- package/server/dist/modules/blocknote.js +103 -7
- package/server/dist/modules/blocknote.js.map +1 -1
- package/server/prisma/schema.prisma +1 -0
- package/server/client/dist/assets/ViewNotes-Duo9XmcJ.js +0 -1
- package/server/client/dist/assets/Views-D4Z_O9Kd.js +0 -1
- package/server/client/dist/assets/trash-CQnj4Rmv.js +0 -1
- package/server/client/dist/assets/view-dashboard-B5fKgvk_.js +0 -99
|
@@ -15,6 +15,7 @@ const MAX_VIEW_PROPERTY_FILTERS = 10;
|
|
|
15
15
|
const DEFAULT_VIEW_DISPLAY_TYPE = "list";
|
|
16
16
|
const DEFAULT_VIEW_SORT_BY = "updatedAt";
|
|
17
17
|
const DEFAULT_VIEW_SORT_ORDER = "desc";
|
|
18
|
+
const DEFAULT_VIEW_TABLE_COLUMNS = ["title", "tags", "properties", "createdAt", "updatedAt"];
|
|
18
19
|
const orderedViewSectionTagsInclude = {
|
|
19
20
|
tags: {
|
|
20
21
|
orderBy: [{ order: "asc" }, { createdAt: "asc" }]
|
|
@@ -44,13 +45,19 @@ const normalizeViewTagNames = (values) => {
|
|
|
44
45
|
return Array.from(new Set(normalizedTagNames.map(normalizeTagName).filter(Boolean)));
|
|
45
46
|
};
|
|
46
47
|
const isViewPropertyFilterOperator = (value) => {
|
|
47
|
-
return value === "equals" || value === "before" || value === "after" || value === "exists" || value === "notExists";
|
|
48
|
+
return value === "equals" || value === "notEquals" || value === "contains" || value === "notContains" || value === "before" || value === "after" || value === "exists" || value === "notExists";
|
|
48
49
|
};
|
|
49
50
|
const isPropertyValueType = (value) => {
|
|
50
51
|
return value === "text" || value === "url" || value === "number" || value === "date" || value === "boolean" || value === "select";
|
|
51
52
|
};
|
|
53
|
+
const isViewTableColumn = (value) => {
|
|
54
|
+
return value === "title" || value === "tags" || value === "properties" || value === "createdAt" || value === "updatedAt";
|
|
55
|
+
};
|
|
52
56
|
const normalizeViewDisplayType = (value) => {
|
|
53
|
-
|
|
57
|
+
if (value === "table" || value === "calendar") {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
return DEFAULT_VIEW_DISPLAY_TYPE;
|
|
54
61
|
};
|
|
55
62
|
const normalizeViewSortBy = (value) => {
|
|
56
63
|
return value === "createdAt" || value === "title" ? value : DEFAULT_VIEW_SORT_BY;
|
|
@@ -58,6 +65,19 @@ const normalizeViewSortBy = (value) => {
|
|
|
58
65
|
const normalizeViewSortOrder = (value) => {
|
|
59
66
|
return value === "asc" ? "asc" : DEFAULT_VIEW_SORT_ORDER;
|
|
60
67
|
};
|
|
68
|
+
const normalizeViewTableColumns = (columns) => {
|
|
69
|
+
const normalizedColumns = (columns ?? []).filter(isViewTableColumn);
|
|
70
|
+
const uniqueColumns = Array.from(new Set(normalizedColumns));
|
|
71
|
+
if (uniqueColumns.length === 0) {
|
|
72
|
+
return [...DEFAULT_VIEW_TABLE_COLUMNS];
|
|
73
|
+
}
|
|
74
|
+
return uniqueColumns.includes("title") ? uniqueColumns : ["title", ...uniqueColumns];
|
|
75
|
+
};
|
|
76
|
+
const normalizeViewDisplayOptions = (options) => {
|
|
77
|
+
return {
|
|
78
|
+
tableColumns: normalizeViewTableColumns(options?.tableColumns)
|
|
79
|
+
};
|
|
80
|
+
};
|
|
61
81
|
const normalizeFilterValue = ({
|
|
62
82
|
value,
|
|
63
83
|
valueType,
|
|
@@ -88,7 +108,10 @@ const normalizeFilterValue = ({
|
|
|
88
108
|
if ((operator === "before" || operator === "after") && valueType !== "date" && valueType !== "number") {
|
|
89
109
|
throw new InvalidNotePropertyInputError("Before and after filters require date or number properties.");
|
|
90
110
|
}
|
|
91
|
-
if (
|
|
111
|
+
if ((operator === "contains" || operator === "notContains") && valueType !== "text" && valueType !== "url") {
|
|
112
|
+
throw new InvalidNotePropertyInputError("Contains filters require text or url properties.");
|
|
113
|
+
}
|
|
114
|
+
if (valueType === "url" && operator !== "contains" && operator !== "notContains") {
|
|
92
115
|
return normalizeUrlValue(normalizedValue);
|
|
93
116
|
}
|
|
94
117
|
return normalizedValue;
|
|
@@ -128,7 +151,8 @@ const parseStoredViewQuery = (value) => {
|
|
|
128
151
|
return {
|
|
129
152
|
propertyFilters: [],
|
|
130
153
|
sortBy: DEFAULT_VIEW_SORT_BY,
|
|
131
|
-
sortOrder: DEFAULT_VIEW_SORT_ORDER
|
|
154
|
+
sortOrder: DEFAULT_VIEW_SORT_ORDER,
|
|
155
|
+
displayOptions: normalizeViewDisplayOptions(null)
|
|
132
156
|
};
|
|
133
157
|
}
|
|
134
158
|
try {
|
|
@@ -136,21 +160,24 @@ const parseStoredViewQuery = (value) => {
|
|
|
136
160
|
return {
|
|
137
161
|
propertyFilters: normalizeViewPropertyFilters(parsed.propertyFilters ?? []),
|
|
138
162
|
sortBy: normalizeViewSortBy(parsed.sortBy),
|
|
139
|
-
sortOrder: normalizeViewSortOrder(parsed.sortOrder)
|
|
163
|
+
sortOrder: normalizeViewSortOrder(parsed.sortOrder),
|
|
164
|
+
displayOptions: normalizeViewDisplayOptions(parsed.displayOptions)
|
|
140
165
|
};
|
|
141
166
|
} catch {
|
|
142
167
|
return {
|
|
143
168
|
propertyFilters: [],
|
|
144
169
|
sortBy: DEFAULT_VIEW_SORT_BY,
|
|
145
|
-
sortOrder: DEFAULT_VIEW_SORT_ORDER
|
|
170
|
+
sortOrder: DEFAULT_VIEW_SORT_ORDER,
|
|
171
|
+
displayOptions: normalizeViewDisplayOptions(null)
|
|
146
172
|
};
|
|
147
173
|
}
|
|
148
174
|
};
|
|
149
|
-
const serializeStoredViewQuery = ({ propertyFilters, sortBy, sortOrder }) => {
|
|
175
|
+
const serializeStoredViewQuery = ({ propertyFilters, sortBy, sortOrder, displayOptions }) => {
|
|
150
176
|
return JSON.stringify({
|
|
151
177
|
propertyFilters,
|
|
152
178
|
sortBy,
|
|
153
|
-
sortOrder
|
|
179
|
+
sortOrder,
|
|
180
|
+
displayOptions
|
|
154
181
|
});
|
|
155
182
|
};
|
|
156
183
|
const clampViewSectionLimit = (value) => {
|
|
@@ -179,9 +206,11 @@ const normalizeViewSectionInput = (input) => {
|
|
|
179
206
|
const trimmedTitle = input.title?.trim() ?? "";
|
|
180
207
|
const sortBy = normalizeViewSortBy(input.sortBy);
|
|
181
208
|
const sortOrder = normalizeViewSortOrder(input.sortOrder);
|
|
209
|
+
const displayOptions = normalizeViewDisplayOptions(input.displayOptions);
|
|
182
210
|
return {
|
|
183
211
|
title: trimmedTitle || buildDefaultSectionTitle(tagNames, propertyFilters),
|
|
184
212
|
displayType: normalizeViewDisplayType(input.displayType),
|
|
213
|
+
displayOptions,
|
|
185
214
|
tagNames,
|
|
186
215
|
mode: input.mode === "or" ? "or" : "and",
|
|
187
216
|
propertyFilters,
|
|
@@ -245,6 +274,7 @@ const serializeViewSection = (section) => {
|
|
|
245
274
|
tabId: String(section.tabId),
|
|
246
275
|
title: section.title,
|
|
247
276
|
displayType: normalizeViewDisplayType(section.displayType),
|
|
277
|
+
displayOptions: query.displayOptions,
|
|
248
278
|
tagNames: section.tags.map((tag) => tag.name),
|
|
249
279
|
mode: section.mode,
|
|
250
280
|
propertyFilters: query.propertyFilters,
|
|
@@ -331,7 +361,8 @@ const buildStoredViewQuery = async (db, section) => {
|
|
|
331
361
|
return serializeStoredViewQuery({
|
|
332
362
|
propertyFilters,
|
|
333
363
|
sortBy: section.sortBy,
|
|
334
|
-
sortOrder: section.sortOrder
|
|
364
|
+
sortOrder: section.sortOrder,
|
|
365
|
+
displayOptions: section.displayOptions
|
|
335
366
|
});
|
|
336
367
|
};
|
|
337
368
|
const getNextTabOrder = async (db) => {
|
|
@@ -397,6 +428,9 @@ const buildPropertyFilterValueWhere = (filter) => {
|
|
|
397
428
|
if (filter.operator === "after") {
|
|
398
429
|
return { numberValue: { gt: numberValue } };
|
|
399
430
|
}
|
|
431
|
+
if (filter.operator === "notEquals") {
|
|
432
|
+
return { numberValue: { not: numberValue } };
|
|
433
|
+
}
|
|
400
434
|
return { numberValue };
|
|
401
435
|
}
|
|
402
436
|
case "date": {
|
|
@@ -407,16 +441,37 @@ const buildPropertyFilterValueWhere = (filter) => {
|
|
|
407
441
|
if (filter.operator === "after") {
|
|
408
442
|
return { dateValue: { gt: dateValue } };
|
|
409
443
|
}
|
|
444
|
+
if (filter.operator === "notEquals") {
|
|
445
|
+
return { dateValue: { not: dateValue } };
|
|
446
|
+
}
|
|
410
447
|
return { dateValue };
|
|
411
448
|
}
|
|
412
|
-
case "boolean":
|
|
449
|
+
case "boolean": {
|
|
450
|
+
if (filter.operator === "notEquals") {
|
|
451
|
+
return { boolValue: { not: filter.value === "true" } };
|
|
452
|
+
}
|
|
413
453
|
return { boolValue: filter.value === "true" };
|
|
454
|
+
}
|
|
414
455
|
case "select":
|
|
456
|
+
if (filter.operator === "notEquals") {
|
|
457
|
+
return { option: { is: { value: { not: normalizeSelectFilterValue(filter.value ?? "") } } } };
|
|
458
|
+
}
|
|
415
459
|
return { option: { is: { value: normalizeSelectFilterValue(filter.value ?? "") } } };
|
|
416
460
|
case "url":
|
|
417
461
|
case "text":
|
|
418
|
-
default:
|
|
419
|
-
|
|
462
|
+
default: {
|
|
463
|
+
const textValue = filter.value?.toLowerCase() ?? "";
|
|
464
|
+
if (filter.operator === "notEquals") {
|
|
465
|
+
return { textValueNormalized: { not: textValue } };
|
|
466
|
+
}
|
|
467
|
+
if (filter.operator === "contains") {
|
|
468
|
+
return { textValueNormalized: { contains: textValue } };
|
|
469
|
+
}
|
|
470
|
+
if (filter.operator === "notContains") {
|
|
471
|
+
return { textValueNormalized: { not: { contains: textValue } } };
|
|
472
|
+
}
|
|
473
|
+
return { textValueNormalized: textValue };
|
|
474
|
+
}
|
|
420
475
|
}
|
|
421
476
|
};
|
|
422
477
|
const buildPropertyFilterWhere = (filter) => {
|
|
@@ -496,22 +551,22 @@ const getViewSectionNotes = async (id, pagination) => {
|
|
|
496
551
|
notes
|
|
497
552
|
};
|
|
498
553
|
};
|
|
499
|
-
const
|
|
554
|
+
const getNotesByPropertiesWithDb = async (db, input, pagination) => {
|
|
500
555
|
const normalizedQuery = normalizeViewNotesQueryInput(input);
|
|
501
556
|
if (normalizedQuery.propertyFilters.length === 0) {
|
|
502
557
|
throw new InvalidNotePropertyInputError("At least one property filter is required.");
|
|
503
558
|
}
|
|
504
559
|
const query = {
|
|
505
560
|
...normalizedQuery,
|
|
506
|
-
propertyFilters: await hydratePropertyFilters(
|
|
561
|
+
propertyFilters: await hydratePropertyFilters(db, normalizedQuery.propertyFilters, {
|
|
507
562
|
validateSelectOptions: true
|
|
508
563
|
})
|
|
509
564
|
};
|
|
510
565
|
const normalizedPagination = normalizeViewNotesPagination(pagination);
|
|
511
566
|
const where = buildViewNotesWhere(query);
|
|
512
567
|
const [totalCount, notes] = await Promise.all([
|
|
513
|
-
|
|
514
|
-
|
|
568
|
+
db.note.count({ where }),
|
|
569
|
+
db.note.findMany({
|
|
515
570
|
orderBy: buildViewNotesOrderBy(query),
|
|
516
571
|
where,
|
|
517
572
|
take: normalizedPagination.limit,
|
|
@@ -523,6 +578,9 @@ const getNotesByProperties = async (input, pagination) => {
|
|
|
523
578
|
notes
|
|
524
579
|
};
|
|
525
580
|
};
|
|
581
|
+
const getNotesByProperties = async (input, pagination) => {
|
|
582
|
+
return getNotesByPropertiesWithDb(models, input, pagination);
|
|
583
|
+
};
|
|
526
584
|
const createViewTab = async (title) => {
|
|
527
585
|
return models.$transaction(async (tx) => {
|
|
528
586
|
const workspace = await ensureViewWorkspace(tx);
|
|
@@ -736,15 +794,18 @@ export {
|
|
|
736
794
|
deleteViewSection,
|
|
737
795
|
deleteViewTab,
|
|
738
796
|
getNotesByProperties,
|
|
797
|
+
getNotesByPropertiesWithDb,
|
|
739
798
|
getViewSectionById,
|
|
740
799
|
getViewSectionNotes,
|
|
741
800
|
getViewWorkspace,
|
|
742
801
|
hydratePropertyFilters,
|
|
802
|
+
normalizeViewDisplayOptions,
|
|
743
803
|
normalizeViewNotesPagination,
|
|
744
804
|
normalizeViewNotesQueryInput,
|
|
745
805
|
normalizeViewPropertyFilters,
|
|
746
806
|
normalizeViewSectionInput,
|
|
747
807
|
normalizeViewTabTitle,
|
|
808
|
+
normalizeViewTableColumns,
|
|
748
809
|
normalizeViewTagNames,
|
|
749
810
|
pickNextActiveViewTabId,
|
|
750
811
|
reorderViewSections,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/view/services/workspace.ts"],"sourcesContent":["import type { Note, Prisma, PropertyValueType } from '@prisma/client';\nimport {\n InvalidNotePropertyInputError,\n normalizePropertyKey,\n normalizeUrlValue,\n} from '~/features/note/services/properties.js';\nimport { buildNoteTagNamesWhere, type NoteTagMatchMode } from '~/features/note/services/tag-filter.js';\nimport models from '~/models.js';\n\nexport type ViewTagMatchMode = NoteTagMatchMode;\nexport type ViewDisplayType = 'list' | 'calendar';\nexport type ViewPropertyFilterOperator = 'equals' | 'before' | 'after' | 'exists' | 'notExists';\nexport type ViewSortBy = 'updatedAt' | 'createdAt' | 'title';\nexport type ViewSortOrder = 'asc' | 'desc';\n\nexport interface ViewPropertyFilterRecord {\n key: string;\n name: string;\n valueType: PropertyValueType;\n operator: ViewPropertyFilterOperator;\n value: string | null;\n}\n\nexport interface ViewSectionRecord {\n id: string;\n tabId: string;\n title: string;\n displayType: ViewDisplayType;\n tagNames: string[];\n mode: ViewTagMatchMode;\n propertyFilters: ViewPropertyFilterRecord[];\n sortBy: ViewSortBy;\n sortOrder: ViewSortOrder;\n limit: number;\n order: number;\n}\n\nexport interface ViewTabRecord {\n id: string;\n title: string;\n order: number;\n sections: ViewSectionRecord[];\n}\n\nexport interface ViewWorkspaceRecord {\n activeTabId: string | null;\n tabs: ViewTabRecord[];\n}\n\nexport interface ViewSectionInput {\n title?: string;\n displayType?: ViewDisplayType;\n tagNames?: string[] | null;\n mode?: ViewTagMatchMode;\n propertyFilters?: ViewPropertyFilterInput[] | null;\n sortBy?: ViewSortBy;\n sortOrder?: ViewSortOrder;\n limit?: number;\n}\n\nexport interface ViewNotesQueryInput {\n tagNames?: string[] | null;\n mode?: ViewTagMatchMode;\n propertyFilters?: ViewPropertyFilterInput[] | null;\n sortBy?: ViewSortBy;\n sortOrder?: ViewSortOrder;\n}\n\nexport interface ViewNotesQueryRecord {\n tagNames: string[];\n mode: ViewTagMatchMode;\n propertyFilters: ViewPropertyFilterRecord[];\n sortBy: ViewSortBy;\n sortOrder: ViewSortOrder;\n}\n\nexport interface ViewPropertyFilterInput {\n key: string;\n operator: ViewPropertyFilterOperator;\n value?: string | null;\n valueType?: PropertyValueType | null;\n}\n\nexport interface ViewSectionNotesResult {\n totalCount: number;\n notes: Note[];\n}\n\nconst VIEW_WORKSPACE_ID = 1;\n\nexport const DEFAULT_VIEW_SECTION_LIMIT = 5;\nexport const MIN_VIEW_SECTION_LIMIT = 1;\nexport const MAX_VIEW_SECTION_LIMIT = 20;\nexport const DEFAULT_VIEW_NOTES_QUERY_LIMIT = 20;\nexport const MAX_VIEW_NOTES_QUERY_LIMIT = 50;\nconst MAX_VIEW_PROPERTY_FILTERS = 10;\nconst DEFAULT_VIEW_DISPLAY_TYPE: ViewDisplayType = 'list';\nconst DEFAULT_VIEW_SORT_BY: ViewSortBy = 'updatedAt';\nconst DEFAULT_VIEW_SORT_ORDER: ViewSortOrder = 'desc';\n\ntype ViewDbClient = typeof models | Prisma.TransactionClient;\n\nconst orderedViewSectionTagsInclude = {\n tags: {\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n },\n} satisfies Prisma.ViewSectionInclude;\n\nconst orderedViewTabSectionsInclude = {\n sections: {\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n include: orderedViewSectionTagsInclude,\n },\n} satisfies Prisma.ViewTabInclude;\n\ntype DbViewSection = Prisma.ViewSectionGetPayload<{\n include: typeof orderedViewSectionTagsInclude;\n}>;\n\ntype DbViewTab = Prisma.ViewTabGetPayload<{\n include: typeof orderedViewTabSectionsInclude;\n}>;\n\nconst normalizeTagName = (value: string) => {\n const trimmedValue = value.trim();\n\n if (!trimmedValue) {\n return '';\n }\n\n if (trimmedValue.startsWith('@')) {\n return trimmedValue;\n }\n\n if (trimmedValue.startsWith('#')) {\n return `@${trimmedValue.slice(1)}`;\n }\n\n return `@${trimmedValue}`;\n};\n\nexport const normalizeViewTagNames = (values: string[]) => {\n const normalizedTagNames = values.flatMap((value) => value.split(','));\n\n return Array.from(new Set(normalizedTagNames.map(normalizeTagName).filter(Boolean)));\n};\n\ninterface StoredViewQuery {\n propertyFilters?: ViewPropertyFilterRecord[];\n sortBy?: ViewSortBy;\n sortOrder?: ViewSortOrder;\n}\n\nconst isViewPropertyFilterOperator = (value: unknown): value is ViewPropertyFilterOperator => {\n return value === 'equals' || value === 'before' || value === 'after' || value === 'exists' || value === 'notExists';\n};\n\nconst isPropertyValueType = (value: unknown): value is PropertyValueType => {\n return (\n value === 'text' ||\n value === 'url' ||\n value === 'number' ||\n value === 'date' ||\n value === 'boolean' ||\n value === 'select'\n );\n};\n\nconst normalizeViewDisplayType = (value: ViewDisplayType | undefined): ViewDisplayType => {\n return value === 'calendar' ? 'calendar' : DEFAULT_VIEW_DISPLAY_TYPE;\n};\n\nconst normalizeViewSortBy = (value: ViewSortBy | undefined): ViewSortBy => {\n return value === 'createdAt' || value === 'title' ? value : DEFAULT_VIEW_SORT_BY;\n};\n\nconst normalizeViewSortOrder = (value: ViewSortOrder | undefined): ViewSortOrder => {\n return value === 'asc' ? 'asc' : DEFAULT_VIEW_SORT_ORDER;\n};\n\nconst normalizeFilterValue = ({\n value,\n valueType,\n operator,\n}: {\n value?: string | null;\n valueType: PropertyValueType;\n operator: ViewPropertyFilterOperator;\n}) => {\n if (operator === 'exists' || operator === 'notExists') {\n return null;\n }\n\n const normalizedValue = String(value ?? '').trim();\n\n if (!normalizedValue) {\n throw new InvalidNotePropertyInputError('Property filter value is required.');\n }\n\n if (valueType === 'number' && !Number.isFinite(Number(normalizedValue))) {\n throw new InvalidNotePropertyInputError('Number property filter value must be finite.');\n }\n\n if (valueType === 'boolean' && normalizedValue !== 'true' && normalizedValue !== 'false') {\n throw new InvalidNotePropertyInputError('Boolean property filter value must be true or false.');\n }\n\n if (valueType === 'date') {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(normalizedValue)) {\n throw new InvalidNotePropertyInputError('Date property filter values must use YYYY-MM-DD.');\n }\n\n const date = new Date(`${normalizedValue}T00:00:00.000Z`);\n\n if (Number.isNaN(date.getTime()) || date.toISOString().slice(0, 10) !== normalizedValue) {\n throw new InvalidNotePropertyInputError('Date property filter value is invalid.');\n }\n }\n\n if ((operator === 'before' || operator === 'after') && valueType !== 'date' && valueType !== 'number') {\n throw new InvalidNotePropertyInputError('Before and after filters require date or number properties.');\n }\n\n if (valueType === 'url') {\n return normalizeUrlValue(normalizedValue);\n }\n\n return normalizedValue;\n};\n\nexport const normalizeViewPropertyFilters = (filters: ViewPropertyFilterInput[] | null | undefined) => {\n const normalizedFilters = filters ?? [];\n\n if (normalizedFilters.length > MAX_VIEW_PROPERTY_FILTERS) {\n throw new InvalidNotePropertyInputError(`A view can have up to ${MAX_VIEW_PROPERTY_FILTERS} property filters.`);\n }\n\n return normalizedFilters\n .map((filter): ViewPropertyFilterRecord | null => {\n const key = normalizePropertyKey(filter.key);\n const valueType = filter.valueType;\n const operator = filter.operator;\n\n if (!isPropertyValueType(valueType)) {\n throw new InvalidNotePropertyInputError('Property filter value type is required.');\n }\n\n if (!isViewPropertyFilterOperator(operator)) {\n throw new InvalidNotePropertyInputError('Property filter operator is invalid.');\n }\n\n const storedName = (filter as { name?: unknown }).name;\n const name = typeof storedName === 'string' && storedName.trim() ? storedName.trim() : key;\n\n return {\n key,\n name,\n valueType,\n operator,\n value: normalizeFilterValue({\n value: filter.value,\n valueType,\n operator,\n }),\n };\n })\n .filter((filter): filter is ViewPropertyFilterRecord => filter !== null);\n};\n\nconst parseStoredViewQuery = (value: string | null): Required<StoredViewQuery> => {\n if (!value) {\n return {\n propertyFilters: [],\n sortBy: DEFAULT_VIEW_SORT_BY,\n sortOrder: DEFAULT_VIEW_SORT_ORDER,\n };\n }\n\n try {\n const parsed = JSON.parse(value) as Partial<StoredViewQuery>;\n\n return {\n propertyFilters: normalizeViewPropertyFilters(parsed.propertyFilters ?? []),\n sortBy: normalizeViewSortBy(parsed.sortBy),\n sortOrder: normalizeViewSortOrder(parsed.sortOrder),\n };\n } catch {\n return {\n propertyFilters: [],\n sortBy: DEFAULT_VIEW_SORT_BY,\n sortOrder: DEFAULT_VIEW_SORT_ORDER,\n };\n }\n};\n\nconst serializeStoredViewQuery = ({ propertyFilters, sortBy, sortOrder }: Required<StoredViewQuery>) => {\n return JSON.stringify({\n propertyFilters,\n sortBy,\n sortOrder,\n });\n};\n\nexport const clampViewSectionLimit = (value: number | undefined) => {\n const numericValue = typeof value === 'number' ? value : Number.NaN;\n\n if (!Number.isInteger(numericValue)) {\n return DEFAULT_VIEW_SECTION_LIMIT;\n }\n\n return Math.min(MAX_VIEW_SECTION_LIMIT, Math.max(MIN_VIEW_SECTION_LIMIT, numericValue));\n};\n\nexport const normalizeViewTabTitle = (title: string) => {\n const trimmedTitle = title.trim();\n\n return trimmedTitle || 'Untitled View';\n};\n\nconst buildDefaultSectionTitle = (tagNames: string[], propertyFilters: ViewPropertyFilterRecord[]) => {\n if (tagNames.length > 0) {\n return tagNames.slice(0, 2).join(' + ');\n }\n\n if (propertyFilters.length > 0) {\n return propertyFilters\n .slice(0, 2)\n .map((filter) => filter.name)\n .join(' + ');\n }\n\n return 'All notes';\n};\n\nexport const normalizeViewSectionInput = (input: ViewSectionInput) => {\n const tagNames = normalizeViewTagNames(input.tagNames ?? []);\n const propertyFilters = normalizeViewPropertyFilters(input.propertyFilters);\n const trimmedTitle = input.title?.trim() ?? '';\n const sortBy = normalizeViewSortBy(input.sortBy);\n const sortOrder = normalizeViewSortOrder(input.sortOrder);\n\n return {\n title: trimmedTitle || buildDefaultSectionTitle(tagNames, propertyFilters),\n displayType: normalizeViewDisplayType(input.displayType),\n tagNames,\n mode: input.mode === 'or' ? 'or' : 'and',\n propertyFilters,\n sortBy,\n sortOrder,\n limit: clampViewSectionLimit(input.limit),\n } satisfies {\n title: string;\n displayType: ViewDisplayType;\n tagNames: string[];\n mode: ViewTagMatchMode;\n propertyFilters: ViewPropertyFilterRecord[];\n sortBy: ViewSortBy;\n sortOrder: ViewSortOrder;\n limit: number;\n };\n};\n\nexport const normalizeViewNotesQueryInput = (input: ViewNotesQueryInput): ViewNotesQueryRecord => {\n return {\n tagNames: normalizeViewTagNames(input.tagNames ?? []),\n mode: input.mode === 'or' ? 'or' : 'and',\n propertyFilters: normalizeViewPropertyFilters(input.propertyFilters),\n sortBy: normalizeViewSortBy(input.sortBy),\n sortOrder: normalizeViewSortOrder(input.sortOrder),\n };\n};\n\nexport const normalizeViewNotesPagination = (pagination?: { limit?: number; offset?: number }) => {\n const numericLimit = Number(pagination?.limit ?? DEFAULT_VIEW_NOTES_QUERY_LIMIT);\n const limit = Number.isInteger(numericLimit)\n ? Math.min(MAX_VIEW_NOTES_QUERY_LIMIT, Math.max(1, numericLimit))\n : DEFAULT_VIEW_NOTES_QUERY_LIMIT;\n const numericOffset = Number(pagination?.offset ?? 0);\n const offset = Number.isInteger(numericOffset) ? Math.max(0, numericOffset) : 0;\n\n return { limit, offset };\n};\n\nexport const pickNextActiveViewTabId = (tabIds: number[], deletedTabId: number, currentActiveTabId: number | null) => {\n const remainingTabIds = tabIds.filter((tabId) => tabId !== deletedTabId);\n\n if (remainingTabIds.length === 0) {\n return null;\n }\n\n if (currentActiveTabId !== deletedTabId) {\n return currentActiveTabId;\n }\n\n return remainingTabIds[0] ?? null;\n};\n\nconst ensureValidReorderIds = (currentIds: number[], nextIds: number[]) => {\n if (currentIds.length !== nextIds.length) {\n throw new Error('Reorder payload must include every item exactly once.');\n }\n\n const currentIdSet = new Set(currentIds);\n const nextIdSet = new Set(nextIds);\n\n if (currentIdSet.size !== currentIds.length || nextIdSet.size !== nextIds.length) {\n throw new Error('Reorder payload contains duplicate ids.');\n }\n\n for (const id of currentIdSet) {\n if (!nextIdSet.has(id)) {\n throw new Error('Reorder payload must match the current item set.');\n }\n }\n};\n\nconst parseViewId = (id: string) => {\n const numericId = Number(id);\n\n if (!Number.isInteger(numericId) || numericId <= 0) {\n throw new Error('A valid id is required.');\n }\n\n return numericId;\n};\n\nconst serializeViewSection = (section: DbViewSection): ViewSectionRecord => {\n const query = parseStoredViewQuery(section.query);\n\n return {\n id: String(section.id),\n tabId: String(section.tabId),\n title: section.title,\n displayType: normalizeViewDisplayType(section.displayType as ViewDisplayType),\n tagNames: section.tags.map((tag) => tag.name),\n mode: section.mode as ViewTagMatchMode,\n propertyFilters: query.propertyFilters,\n sortBy: query.sortBy,\n sortOrder: query.sortOrder,\n limit: section.limit,\n order: section.order,\n };\n};\n\nconst serializeViewTab = (tab: DbViewTab): ViewTabRecord => ({\n id: String(tab.id),\n title: tab.title,\n order: tab.order,\n sections: tab.sections.map(serializeViewSection),\n});\n\nconst getOrderedViewTabs = async (db: ViewDbClient) => {\n return db.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n include: orderedViewTabSectionsInclude,\n });\n};\n\nconst ensureViewWorkspace = async (db: ViewDbClient) => {\n return db.viewWorkspace.upsert({\n where: { id: VIEW_WORKSPACE_ID },\n update: {},\n create: { id: VIEW_WORKSPACE_ID },\n });\n};\n\nconst readViewWorkspace = async (db: ViewDbClient): Promise<ViewWorkspaceRecord> => {\n const [workspace, tabs] = await Promise.all([ensureViewWorkspace(db), getOrderedViewTabs(db)]);\n\n const activeTabId =\n workspace.activeTabId !== null && tabs.some((tab) => tab.id === workspace.activeTabId)\n ? String(workspace.activeTabId)\n : tabs[0]\n ? String(tabs[0].id)\n : null;\n\n return {\n activeTabId,\n tabs: tabs.map(serializeViewTab),\n };\n};\n\nconst readViewSection = async (db: ViewDbClient, id: number): Promise<ViewSectionRecord | null> => {\n const section = await db.viewSection.findUnique({\n where: { id },\n include: orderedViewSectionTagsInclude,\n });\n\n return section ? serializeViewSection(section) : null;\n};\n\nexport const hydratePropertyFilters = async (\n db: ViewDbClient,\n filters: ViewPropertyFilterRecord[],\n options: { validateSelectOptions?: boolean } = {},\n) => {\n if (filters.length === 0) {\n return [];\n }\n\n const definitions = await db.propertyDefinition.findMany({\n where: { key: { in: filters.map((filter) => filter.key) } },\n select: {\n key: true,\n name: true,\n valueType: true,\n options: {\n select: {\n value: true,\n },\n },\n },\n });\n const definitionByKey = new Map(definitions.map((definition) => [definition.key, definition]));\n\n return filters.map((filter) => {\n const definition = definitionByKey.get(filter.key);\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(`Property ${filter.key} is not defined.`);\n }\n\n if (definition.valueType !== filter.valueType) {\n throw new InvalidNotePropertyInputError(`Property ${filter.key} uses ${definition.valueType} values.`);\n }\n\n if (\n options.validateSelectOptions &&\n filter.valueType === 'select' &&\n filter.operator !== 'exists' &&\n filter.operator !== 'notExists'\n ) {\n const optionValue = normalizeSelectFilterValue(filter.value ?? '');\n\n if (!definition.options.some((option) => option.value === optionValue)) {\n throw new InvalidNotePropertyInputError(`Property ${filter.key} option ${optionValue} is not defined.`);\n }\n }\n\n return {\n ...filter,\n name: definition.name,\n };\n });\n};\n\nconst buildStoredViewQuery = async (db: ViewDbClient, section: ReturnType<typeof normalizeViewSectionInput>) => {\n const propertyFilters = await hydratePropertyFilters(db, section.propertyFilters, { validateSelectOptions: true });\n\n return serializeStoredViewQuery({\n propertyFilters,\n sortBy: section.sortBy,\n sortOrder: section.sortOrder,\n });\n};\n\nconst getNextTabOrder = async (db: ViewDbClient) => {\n const lastTab = await db.viewTab.findFirst({\n orderBy: [{ order: 'desc' }, { createdAt: 'desc' }],\n select: { order: true },\n });\n\n return lastTab ? lastTab.order + 1 : 0;\n};\n\nconst getNextSectionOrder = async (db: ViewDbClient, tabId: number) => {\n const lastSection = await db.viewSection.findFirst({\n where: { tabId },\n orderBy: [{ order: 'desc' }, { createdAt: 'desc' }],\n select: { order: true },\n });\n\n return lastSection ? lastSection.order + 1 : 0;\n};\n\nconst resequenceViewTabs = async (db: ViewDbClient) => {\n const tabs = await db.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n\n await Promise.all(\n tabs.map((tab, index) =>\n db.viewTab.update({\n where: { id: tab.id },\n data: { order: index },\n }),\n ),\n );\n};\n\nconst resequenceViewSections = async (db: ViewDbClient, tabId: number) => {\n const sections = await db.viewSection.findMany({\n where: { tabId },\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n\n await Promise.all(\n sections.map((section, index) =>\n db.viewSection.update({\n where: { id: section.id },\n data: { order: index },\n }),\n ),\n );\n};\n\nexport const getViewWorkspace = async () => {\n return readViewWorkspace(models);\n};\n\nexport const getViewSectionById = async (id: string) => {\n return readViewSection(models, parseViewId(id));\n};\n\nconst normalizeSelectFilterValue = (value: string) => {\n return value.trim().toLowerCase().replace(/\\s+/g, '-');\n};\n\nconst buildPropertyFilterValueWhere = (filter: ViewPropertyFilterRecord): Prisma.NotePropertyWhereInput => {\n switch (filter.valueType) {\n case 'number': {\n const numberValue = Number(filter.value);\n\n if (filter.operator === 'before') {\n return { numberValue: { lt: numberValue } };\n }\n\n if (filter.operator === 'after') {\n return { numberValue: { gt: numberValue } };\n }\n\n return { numberValue };\n }\n case 'date': {\n const dateValue = new Date(`${filter.value}T00:00:00.000Z`);\n\n if (filter.operator === 'before') {\n return { dateValue: { lt: dateValue } };\n }\n\n if (filter.operator === 'after') {\n return { dateValue: { gt: dateValue } };\n }\n\n return { dateValue };\n }\n case 'boolean':\n return { boolValue: filter.value === 'true' };\n case 'select':\n return { option: { is: { value: normalizeSelectFilterValue(filter.value ?? '') } } };\n case 'url':\n case 'text':\n default:\n return { textValueNormalized: filter.value?.toLowerCase() ?? '' };\n }\n};\n\nexport const buildPropertyFilterWhere = (filter: ViewPropertyFilterRecord): Prisma.NoteWhereInput => {\n const definitionWhere = {\n definition: {\n is: {\n key: filter.key,\n },\n },\n } satisfies Prisma.NotePropertyWhereInput;\n\n if (filter.operator === 'notExists') {\n return {\n properties: {\n none: definitionWhere,\n },\n };\n }\n\n if (filter.operator === 'exists') {\n return {\n properties: {\n some: definitionWhere,\n },\n };\n }\n\n return {\n properties: {\n some: {\n ...definitionWhere,\n ...buildPropertyFilterValueWhere(filter),\n },\n },\n };\n};\n\nexport const buildViewNotesWhere = (\n query: Pick<ViewNotesQueryRecord, 'tagNames' | 'mode' | 'propertyFilters'>,\n): Prisma.NoteWhereInput => {\n const clauses: Prisma.NoteWhereInput[] = [];\n\n if (query.tagNames.length > 0) {\n clauses.push(buildNoteTagNamesWhere(query.tagNames, query.mode as NoteTagMatchMode));\n }\n\n for (const filter of query.propertyFilters) {\n clauses.push(buildPropertyFilterWhere(filter));\n }\n\n if (clauses.length === 0) {\n return {};\n }\n\n return { AND: clauses };\n};\n\nexport const buildViewSectionWhere = (section: ViewSectionRecord): Prisma.NoteWhereInput => {\n return buildViewNotesWhere(section);\n};\n\nconst buildViewNotesOrderBy = (\n query: Pick<ViewNotesQueryRecord, 'sortBy' | 'sortOrder'>,\n): Prisma.NoteOrderByWithRelationInput[] => {\n return [{ [query.sortBy]: query.sortOrder }, { id: 'asc' }];\n};\n\nconst buildViewSectionOrderBy = (section: ViewSectionRecord): Prisma.NoteOrderByWithRelationInput[] => {\n return buildViewNotesOrderBy(section);\n};\n\nexport const getViewSectionNotes = async (\n id: string,\n pagination: {\n limit: number;\n offset: number;\n },\n): Promise<ViewSectionNotesResult | null> => {\n const section = await models.viewSection.findUnique({\n where: { id: parseViewId(id) },\n include: orderedViewSectionTagsInclude,\n });\n\n if (!section) {\n return null;\n }\n\n const serializedSection = serializeViewSection(section);\n const where = buildViewSectionWhere(serializedSection);\n\n const [totalCount, notes] = await Promise.all([\n models.note.count({ where }),\n models.note.findMany({\n orderBy: buildViewSectionOrderBy(serializedSection),\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset),\n }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n};\n\nexport const getNotesByProperties = async (\n input: ViewNotesQueryInput,\n pagination?: {\n limit?: number;\n offset?: number;\n },\n): Promise<ViewSectionNotesResult> => {\n const normalizedQuery = normalizeViewNotesQueryInput(input);\n\n if (normalizedQuery.propertyFilters.length === 0) {\n throw new InvalidNotePropertyInputError('At least one property filter is required.');\n }\n\n const query = {\n ...normalizedQuery,\n propertyFilters: await hydratePropertyFilters(models, normalizedQuery.propertyFilters, {\n validateSelectOptions: true,\n }),\n };\n const normalizedPagination = normalizeViewNotesPagination(pagination);\n const where = buildViewNotesWhere(query);\n\n const [totalCount, notes] = await Promise.all([\n models.note.count({ where }),\n models.note.findMany({\n orderBy: buildViewNotesOrderBy(query),\n where,\n take: normalizedPagination.limit,\n skip: normalizedPagination.offset,\n }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n};\n\nexport const createViewTab = async (title: string) => {\n return models.$transaction(async (tx) => {\n const workspace = await ensureViewWorkspace(tx);\n const nextTabOrder = await getNextTabOrder(tx);\n const createdTab = await tx.viewTab.create({\n data: {\n title: normalizeViewTabTitle(title),\n order: nextTabOrder,\n },\n include: orderedViewTabSectionsInclude,\n });\n\n if (workspace.activeTabId === null) {\n await tx.viewWorkspace.update({\n where: { id: VIEW_WORKSPACE_ID },\n data: { activeTabId: createdTab.id },\n });\n }\n\n return serializeViewTab(createdTab);\n });\n};\n\nexport const updateViewTab = async (id: string, title: string) => {\n const updatedTab = await models.viewTab.update({\n where: { id: parseViewId(id) },\n data: {\n title: normalizeViewTabTitle(title),\n },\n include: orderedViewTabSectionsInclude,\n });\n\n return serializeViewTab(updatedTab);\n};\n\nexport const deleteViewTab = async (id: string) => {\n const tabId = parseViewId(id);\n\n return models.$transaction(async (tx) => {\n const workspace = await ensureViewWorkspace(tx);\n const tabs = await tx.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n\n if (!tabs.some((tab) => tab.id === tabId)) {\n return false;\n }\n\n await tx.viewTab.delete({ where: { id: tabId } });\n await resequenceViewTabs(tx);\n\n const nextActiveTabId = pickNextActiveViewTabId(\n tabs.map((tab) => tab.id),\n tabId,\n workspace.activeTabId,\n );\n\n await tx.viewWorkspace.update({\n where: { id: VIEW_WORKSPACE_ID },\n data: { activeTabId: nextActiveTabId },\n });\n\n return true;\n });\n};\n\nexport const setActiveViewTab = async (id: string) => {\n const tabId = parseViewId(id);\n\n return models.$transaction(async (tx) => {\n const workspace = await ensureViewWorkspace(tx);\n const tab = await tx.viewTab.findUnique({\n where: { id: tabId },\n select: { id: true },\n });\n\n if (!tab) {\n throw new Error('View tab not found.');\n }\n\n await tx.viewWorkspace.update({\n where: { id: workspace.id },\n data: { activeTabId: tabId },\n });\n\n return readViewWorkspace(tx);\n });\n};\n\nexport const reorderViewTabs = async (tabIds: string[]) => {\n const nextTabIds = tabIds.map(parseViewId);\n\n return models.$transaction(async (tx) => {\n const tabs = await tx.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n const currentTabIds = tabs.map((tab) => tab.id);\n\n ensureValidReorderIds(currentTabIds, nextTabIds);\n\n await Promise.all(\n nextTabIds.map((tabId, index) =>\n tx.viewTab.update({\n where: { id: tabId },\n data: { order: index },\n }),\n ),\n );\n\n const reorderedTabs = await getOrderedViewTabs(tx);\n return reorderedTabs.map(serializeViewTab);\n });\n};\n\nexport const createViewSection = async (tabId: string, input: ViewSectionInput) => {\n const numericTabId = parseViewId(tabId);\n const nextSection = normalizeViewSectionInput(input);\n\n return models.$transaction(async (tx) => {\n const tab = await tx.viewTab.findUnique({\n where: { id: numericTabId },\n select: { id: true },\n });\n\n if (!tab) {\n throw new Error('View tab not found.');\n }\n\n const nextSectionOrder = await getNextSectionOrder(tx, numericTabId);\n const query = await buildStoredViewQuery(tx, nextSection);\n\n const createdSection = await tx.viewSection.create({\n data: {\n tabId: numericTabId,\n title: nextSection.title,\n displayType: nextSection.displayType,\n query,\n mode: nextSection.mode,\n limit: nextSection.limit,\n order: nextSectionOrder,\n tags: {\n create: nextSection.tagNames.map((tagName, index) => ({\n name: tagName,\n order: index,\n })),\n },\n },\n include: orderedViewSectionTagsInclude,\n });\n\n return serializeViewSection(createdSection);\n });\n};\n\nexport const updateViewSection = async (id: string, input: ViewSectionInput) => {\n const sectionId = parseViewId(id);\n const nextSection = normalizeViewSectionInput(input);\n\n return models.$transaction(async (tx) => {\n const query = await buildStoredViewQuery(tx, nextSection);\n\n const updatedSection = await tx.viewSection.update({\n where: { id: sectionId },\n data: {\n title: nextSection.title,\n displayType: nextSection.displayType,\n query,\n mode: nextSection.mode,\n limit: nextSection.limit,\n tags: {\n deleteMany: {},\n create: nextSection.tagNames.map((tagName, index) => ({\n name: tagName,\n order: index,\n })),\n },\n },\n include: orderedViewSectionTagsInclude,\n });\n\n return serializeViewSection(updatedSection);\n });\n};\n\nexport const deleteViewSection = async (id: string) => {\n const sectionId = parseViewId(id);\n\n return models.$transaction(async (tx) => {\n const section = await tx.viewSection.findUnique({\n where: { id: sectionId },\n select: { id: true, tabId: true },\n });\n\n if (!section) {\n return false;\n }\n\n await tx.viewSection.delete({\n where: { id: sectionId },\n });\n await resequenceViewSections(tx, section.tabId);\n\n return true;\n });\n};\n\nexport const reorderViewSections = async (tabId: string, sectionIds: string[]) => {\n const numericTabId = parseViewId(tabId);\n const nextSectionIds = sectionIds.map(parseViewId);\n\n return models.$transaction(async (tx) => {\n const sections = await tx.viewSection.findMany({\n where: { tabId: numericTabId },\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n const currentSectionIds = sections.map((section) => section.id);\n\n ensureValidReorderIds(currentSectionIds, nextSectionIds);\n\n await Promise.all(\n nextSectionIds.map((sectionId, index) =>\n tx.viewSection.update({\n where: { id: sectionId },\n data: { order: index },\n }),\n ),\n );\n\n const reorderedSections = await tx.viewSection.findMany({\n where: { tabId: numericTabId },\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n include: orderedViewSectionTagsInclude,\n });\n\n return reorderedSections.map(serializeViewSection);\n });\n};\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/view/services/workspace.ts"],"sourcesContent":["import type { Note, Prisma, PropertyValueType } from '@prisma/client';\nimport {\n InvalidNotePropertyInputError,\n normalizePropertyKey,\n normalizeUrlValue,\n} from '~/features/note/services/properties.js';\nimport { buildNoteTagNamesWhere, type NoteTagMatchMode } from '~/features/note/services/tag-filter.js';\nimport models from '~/models.js';\n\nexport type ViewTagMatchMode = NoteTagMatchMode;\nexport type ViewDisplayType = 'list' | 'table' | 'calendar';\nexport type ViewTableColumn = 'title' | 'tags' | 'properties' | 'createdAt' | 'updatedAt';\nexport type ViewPropertyFilterOperator =\n | 'equals'\n | 'notEquals'\n | 'contains'\n | 'notContains'\n | 'before'\n | 'after'\n | 'exists'\n | 'notExists';\nexport type ViewSortBy = 'updatedAt' | 'createdAt' | 'title';\nexport type ViewSortOrder = 'asc' | 'desc';\n\nexport interface ViewDisplayOptionsRecord {\n tableColumns: ViewTableColumn[];\n}\n\nexport interface ViewPropertyFilterRecord {\n key: string;\n name: string;\n valueType: PropertyValueType;\n operator: ViewPropertyFilterOperator;\n value: string | null;\n}\n\nexport interface ViewSectionRecord {\n id: string;\n tabId: string;\n title: string;\n displayType: ViewDisplayType;\n displayOptions: ViewDisplayOptionsRecord;\n tagNames: string[];\n mode: ViewTagMatchMode;\n propertyFilters: ViewPropertyFilterRecord[];\n sortBy: ViewSortBy;\n sortOrder: ViewSortOrder;\n limit: number;\n order: number;\n}\n\nexport interface ViewTabRecord {\n id: string;\n title: string;\n order: number;\n sections: ViewSectionRecord[];\n}\n\nexport interface ViewWorkspaceRecord {\n activeTabId: string | null;\n tabs: ViewTabRecord[];\n}\n\nexport interface ViewSectionInput {\n title?: string;\n displayType?: ViewDisplayType;\n displayOptions?: ViewDisplayOptionsInput | null;\n tagNames?: string[] | null;\n mode?: ViewTagMatchMode;\n propertyFilters?: ViewPropertyFilterInput[] | null;\n sortBy?: ViewSortBy;\n sortOrder?: ViewSortOrder;\n limit?: number;\n}\n\nexport interface ViewNotesQueryInput {\n tagNames?: string[] | null;\n mode?: ViewTagMatchMode;\n propertyFilters?: ViewPropertyFilterInput[] | null;\n sortBy?: ViewSortBy;\n sortOrder?: ViewSortOrder;\n}\n\nexport interface ViewNotesQueryRecord {\n tagNames: string[];\n mode: ViewTagMatchMode;\n propertyFilters: ViewPropertyFilterRecord[];\n sortBy: ViewSortBy;\n sortOrder: ViewSortOrder;\n}\n\nexport interface ViewPropertyFilterInput {\n key: string;\n operator: ViewPropertyFilterOperator;\n value?: string | null;\n valueType?: PropertyValueType | null;\n}\n\nexport interface ViewDisplayOptionsInput {\n tableColumns?: ViewTableColumn[] | null;\n}\n\nexport interface ViewSectionNotesResult {\n totalCount: number;\n notes: Note[];\n}\n\nconst VIEW_WORKSPACE_ID = 1;\n\nexport const DEFAULT_VIEW_SECTION_LIMIT = 5;\nexport const MIN_VIEW_SECTION_LIMIT = 1;\nexport const MAX_VIEW_SECTION_LIMIT = 20;\nexport const DEFAULT_VIEW_NOTES_QUERY_LIMIT = 20;\nexport const MAX_VIEW_NOTES_QUERY_LIMIT = 50;\nconst MAX_VIEW_PROPERTY_FILTERS = 10;\nconst DEFAULT_VIEW_DISPLAY_TYPE: ViewDisplayType = 'list';\nconst DEFAULT_VIEW_SORT_BY: ViewSortBy = 'updatedAt';\nconst DEFAULT_VIEW_SORT_ORDER: ViewSortOrder = 'desc';\nconst DEFAULT_VIEW_TABLE_COLUMNS: ViewTableColumn[] = ['title', 'tags', 'properties', 'createdAt', 'updatedAt'];\n\ntype ViewDbClient = typeof models | Prisma.TransactionClient;\n\nconst orderedViewSectionTagsInclude = {\n tags: {\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n },\n} satisfies Prisma.ViewSectionInclude;\n\nconst orderedViewTabSectionsInclude = {\n sections: {\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n include: orderedViewSectionTagsInclude,\n },\n} satisfies Prisma.ViewTabInclude;\n\ntype DbViewSection = Prisma.ViewSectionGetPayload<{\n include: typeof orderedViewSectionTagsInclude;\n}>;\n\ntype DbViewTab = Prisma.ViewTabGetPayload<{\n include: typeof orderedViewTabSectionsInclude;\n}>;\n\nconst normalizeTagName = (value: string) => {\n const trimmedValue = value.trim();\n\n if (!trimmedValue) {\n return '';\n }\n\n if (trimmedValue.startsWith('@')) {\n return trimmedValue;\n }\n\n if (trimmedValue.startsWith('#')) {\n return `@${trimmedValue.slice(1)}`;\n }\n\n return `@${trimmedValue}`;\n};\n\nexport const normalizeViewTagNames = (values: string[]) => {\n const normalizedTagNames = values.flatMap((value) => value.split(','));\n\n return Array.from(new Set(normalizedTagNames.map(normalizeTagName).filter(Boolean)));\n};\n\ninterface StoredViewQuery {\n propertyFilters?: ViewPropertyFilterRecord[];\n sortBy?: ViewSortBy;\n sortOrder?: ViewSortOrder;\n displayOptions?: Partial<ViewDisplayOptionsRecord>;\n}\n\ninterface ParsedStoredViewQuery {\n propertyFilters: ViewPropertyFilterRecord[];\n sortBy: ViewSortBy;\n sortOrder: ViewSortOrder;\n displayOptions: ViewDisplayOptionsRecord;\n}\n\nconst isViewPropertyFilterOperator = (value: unknown): value is ViewPropertyFilterOperator => {\n return (\n value === 'equals' ||\n value === 'notEquals' ||\n value === 'contains' ||\n value === 'notContains' ||\n value === 'before' ||\n value === 'after' ||\n value === 'exists' ||\n value === 'notExists'\n );\n};\n\nconst isPropertyValueType = (value: unknown): value is PropertyValueType => {\n return (\n value === 'text' ||\n value === 'url' ||\n value === 'number' ||\n value === 'date' ||\n value === 'boolean' ||\n value === 'select'\n );\n};\n\nconst isViewTableColumn = (value: unknown): value is ViewTableColumn => {\n return (\n value === 'title' ||\n value === 'tags' ||\n value === 'properties' ||\n value === 'createdAt' ||\n value === 'updatedAt'\n );\n};\n\nconst normalizeViewDisplayType = (value: ViewDisplayType | undefined): ViewDisplayType => {\n if (value === 'table' || value === 'calendar') {\n return value;\n }\n\n return DEFAULT_VIEW_DISPLAY_TYPE;\n};\n\nconst normalizeViewSortBy = (value: ViewSortBy | undefined): ViewSortBy => {\n return value === 'createdAt' || value === 'title' ? value : DEFAULT_VIEW_SORT_BY;\n};\n\nconst normalizeViewSortOrder = (value: ViewSortOrder | undefined): ViewSortOrder => {\n return value === 'asc' ? 'asc' : DEFAULT_VIEW_SORT_ORDER;\n};\n\nexport const normalizeViewTableColumns = (columns: ViewTableColumn[] | null | undefined): ViewTableColumn[] => {\n const normalizedColumns = (columns ?? []).filter(isViewTableColumn);\n const uniqueColumns = Array.from(new Set(normalizedColumns));\n\n if (uniqueColumns.length === 0) {\n return [...DEFAULT_VIEW_TABLE_COLUMNS];\n }\n\n return uniqueColumns.includes('title') ? uniqueColumns : ['title' as const, ...uniqueColumns];\n};\n\nexport const normalizeViewDisplayOptions = (\n options: ViewDisplayOptionsInput | Partial<ViewDisplayOptionsRecord> | null | undefined,\n): ViewDisplayOptionsRecord => {\n return {\n tableColumns: normalizeViewTableColumns(options?.tableColumns),\n };\n};\n\nconst normalizeFilterValue = ({\n value,\n valueType,\n operator,\n}: {\n value?: string | null;\n valueType: PropertyValueType;\n operator: ViewPropertyFilterOperator;\n}) => {\n if (operator === 'exists' || operator === 'notExists') {\n return null;\n }\n\n const normalizedValue = String(value ?? '').trim();\n\n if (!normalizedValue) {\n throw new InvalidNotePropertyInputError('Property filter value is required.');\n }\n\n if (valueType === 'number' && !Number.isFinite(Number(normalizedValue))) {\n throw new InvalidNotePropertyInputError('Number property filter value must be finite.');\n }\n\n if (valueType === 'boolean' && normalizedValue !== 'true' && normalizedValue !== 'false') {\n throw new InvalidNotePropertyInputError('Boolean property filter value must be true or false.');\n }\n\n if (valueType === 'date') {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(normalizedValue)) {\n throw new InvalidNotePropertyInputError('Date property filter values must use YYYY-MM-DD.');\n }\n\n const date = new Date(`${normalizedValue}T00:00:00.000Z`);\n\n if (Number.isNaN(date.getTime()) || date.toISOString().slice(0, 10) !== normalizedValue) {\n throw new InvalidNotePropertyInputError('Date property filter value is invalid.');\n }\n }\n\n if ((operator === 'before' || operator === 'after') && valueType !== 'date' && valueType !== 'number') {\n throw new InvalidNotePropertyInputError('Before and after filters require date or number properties.');\n }\n\n if ((operator === 'contains' || operator === 'notContains') && valueType !== 'text' && valueType !== 'url') {\n throw new InvalidNotePropertyInputError('Contains filters require text or url properties.');\n }\n\n if (valueType === 'url' && operator !== 'contains' && operator !== 'notContains') {\n return normalizeUrlValue(normalizedValue);\n }\n\n return normalizedValue;\n};\n\nexport const normalizeViewPropertyFilters = (filters: ViewPropertyFilterInput[] | null | undefined) => {\n const normalizedFilters = filters ?? [];\n\n if (normalizedFilters.length > MAX_VIEW_PROPERTY_FILTERS) {\n throw new InvalidNotePropertyInputError(`A view can have up to ${MAX_VIEW_PROPERTY_FILTERS} property filters.`);\n }\n\n return normalizedFilters\n .map((filter): ViewPropertyFilterRecord | null => {\n const key = normalizePropertyKey(filter.key);\n const valueType = filter.valueType;\n const operator = filter.operator;\n\n if (!isPropertyValueType(valueType)) {\n throw new InvalidNotePropertyInputError('Property filter value type is required.');\n }\n\n if (!isViewPropertyFilterOperator(operator)) {\n throw new InvalidNotePropertyInputError('Property filter operator is invalid.');\n }\n\n const storedName = (filter as { name?: unknown }).name;\n const name = typeof storedName === 'string' && storedName.trim() ? storedName.trim() : key;\n\n return {\n key,\n name,\n valueType,\n operator,\n value: normalizeFilterValue({\n value: filter.value,\n valueType,\n operator,\n }),\n };\n })\n .filter((filter): filter is ViewPropertyFilterRecord => filter !== null);\n};\n\nconst parseStoredViewQuery = (value: string | null): ParsedStoredViewQuery => {\n if (!value) {\n return {\n propertyFilters: [],\n sortBy: DEFAULT_VIEW_SORT_BY,\n sortOrder: DEFAULT_VIEW_SORT_ORDER,\n displayOptions: normalizeViewDisplayOptions(null),\n };\n }\n\n try {\n const parsed = JSON.parse(value) as Partial<StoredViewQuery>;\n\n return {\n propertyFilters: normalizeViewPropertyFilters(parsed.propertyFilters ?? []),\n sortBy: normalizeViewSortBy(parsed.sortBy),\n sortOrder: normalizeViewSortOrder(parsed.sortOrder),\n displayOptions: normalizeViewDisplayOptions(parsed.displayOptions),\n };\n } catch {\n return {\n propertyFilters: [],\n sortBy: DEFAULT_VIEW_SORT_BY,\n sortOrder: DEFAULT_VIEW_SORT_ORDER,\n displayOptions: normalizeViewDisplayOptions(null),\n };\n }\n};\n\nconst serializeStoredViewQuery = ({ propertyFilters, sortBy, sortOrder, displayOptions }: ParsedStoredViewQuery) => {\n return JSON.stringify({\n propertyFilters,\n sortBy,\n sortOrder,\n displayOptions,\n });\n};\n\nexport const clampViewSectionLimit = (value: number | undefined) => {\n const numericValue = typeof value === 'number' ? value : Number.NaN;\n\n if (!Number.isInteger(numericValue)) {\n return DEFAULT_VIEW_SECTION_LIMIT;\n }\n\n return Math.min(MAX_VIEW_SECTION_LIMIT, Math.max(MIN_VIEW_SECTION_LIMIT, numericValue));\n};\n\nexport const normalizeViewTabTitle = (title: string) => {\n const trimmedTitle = title.trim();\n\n return trimmedTitle || 'Untitled View';\n};\n\nconst buildDefaultSectionTitle = (tagNames: string[], propertyFilters: ViewPropertyFilterRecord[]) => {\n if (tagNames.length > 0) {\n return tagNames.slice(0, 2).join(' + ');\n }\n\n if (propertyFilters.length > 0) {\n return propertyFilters\n .slice(0, 2)\n .map((filter) => filter.name)\n .join(' + ');\n }\n\n return 'All notes';\n};\n\nexport const normalizeViewSectionInput = (input: ViewSectionInput) => {\n const tagNames = normalizeViewTagNames(input.tagNames ?? []);\n const propertyFilters = normalizeViewPropertyFilters(input.propertyFilters);\n const trimmedTitle = input.title?.trim() ?? '';\n const sortBy = normalizeViewSortBy(input.sortBy);\n const sortOrder = normalizeViewSortOrder(input.sortOrder);\n const displayOptions = normalizeViewDisplayOptions(input.displayOptions);\n\n return {\n title: trimmedTitle || buildDefaultSectionTitle(tagNames, propertyFilters),\n displayType: normalizeViewDisplayType(input.displayType),\n displayOptions,\n tagNames,\n mode: input.mode === 'or' ? 'or' : 'and',\n propertyFilters,\n sortBy,\n sortOrder,\n limit: clampViewSectionLimit(input.limit),\n } satisfies {\n title: string;\n displayType: ViewDisplayType;\n displayOptions: ViewDisplayOptionsRecord;\n tagNames: string[];\n mode: ViewTagMatchMode;\n propertyFilters: ViewPropertyFilterRecord[];\n sortBy: ViewSortBy;\n sortOrder: ViewSortOrder;\n limit: number;\n };\n};\n\nexport const normalizeViewNotesQueryInput = (input: ViewNotesQueryInput): ViewNotesQueryRecord => {\n return {\n tagNames: normalizeViewTagNames(input.tagNames ?? []),\n mode: input.mode === 'or' ? 'or' : 'and',\n propertyFilters: normalizeViewPropertyFilters(input.propertyFilters),\n sortBy: normalizeViewSortBy(input.sortBy),\n sortOrder: normalizeViewSortOrder(input.sortOrder),\n };\n};\n\nexport const normalizeViewNotesPagination = (pagination?: { limit?: number; offset?: number }) => {\n const numericLimit = Number(pagination?.limit ?? DEFAULT_VIEW_NOTES_QUERY_LIMIT);\n const limit = Number.isInteger(numericLimit)\n ? Math.min(MAX_VIEW_NOTES_QUERY_LIMIT, Math.max(1, numericLimit))\n : DEFAULT_VIEW_NOTES_QUERY_LIMIT;\n const numericOffset = Number(pagination?.offset ?? 0);\n const offset = Number.isInteger(numericOffset) ? Math.max(0, numericOffset) : 0;\n\n return { limit, offset };\n};\n\nexport const pickNextActiveViewTabId = (tabIds: number[], deletedTabId: number, currentActiveTabId: number | null) => {\n const remainingTabIds = tabIds.filter((tabId) => tabId !== deletedTabId);\n\n if (remainingTabIds.length === 0) {\n return null;\n }\n\n if (currentActiveTabId !== deletedTabId) {\n return currentActiveTabId;\n }\n\n return remainingTabIds[0] ?? null;\n};\n\nconst ensureValidReorderIds = (currentIds: number[], nextIds: number[]) => {\n if (currentIds.length !== nextIds.length) {\n throw new Error('Reorder payload must include every item exactly once.');\n }\n\n const currentIdSet = new Set(currentIds);\n const nextIdSet = new Set(nextIds);\n\n if (currentIdSet.size !== currentIds.length || nextIdSet.size !== nextIds.length) {\n throw new Error('Reorder payload contains duplicate ids.');\n }\n\n for (const id of currentIdSet) {\n if (!nextIdSet.has(id)) {\n throw new Error('Reorder payload must match the current item set.');\n }\n }\n};\n\nconst parseViewId = (id: string) => {\n const numericId = Number(id);\n\n if (!Number.isInteger(numericId) || numericId <= 0) {\n throw new Error('A valid id is required.');\n }\n\n return numericId;\n};\n\nconst serializeViewSection = (section: DbViewSection): ViewSectionRecord => {\n const query = parseStoredViewQuery(section.query);\n\n return {\n id: String(section.id),\n tabId: String(section.tabId),\n title: section.title,\n displayType: normalizeViewDisplayType(section.displayType as ViewDisplayType),\n displayOptions: query.displayOptions,\n tagNames: section.tags.map((tag) => tag.name),\n mode: section.mode as ViewTagMatchMode,\n propertyFilters: query.propertyFilters,\n sortBy: query.sortBy,\n sortOrder: query.sortOrder,\n limit: section.limit,\n order: section.order,\n };\n};\n\nconst serializeViewTab = (tab: DbViewTab): ViewTabRecord => ({\n id: String(tab.id),\n title: tab.title,\n order: tab.order,\n sections: tab.sections.map(serializeViewSection),\n});\n\nconst getOrderedViewTabs = async (db: ViewDbClient) => {\n return db.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n include: orderedViewTabSectionsInclude,\n });\n};\n\nconst ensureViewWorkspace = async (db: ViewDbClient) => {\n return db.viewWorkspace.upsert({\n where: { id: VIEW_WORKSPACE_ID },\n update: {},\n create: { id: VIEW_WORKSPACE_ID },\n });\n};\n\nconst readViewWorkspace = async (db: ViewDbClient): Promise<ViewWorkspaceRecord> => {\n const [workspace, tabs] = await Promise.all([ensureViewWorkspace(db), getOrderedViewTabs(db)]);\n\n const activeTabId =\n workspace.activeTabId !== null && tabs.some((tab) => tab.id === workspace.activeTabId)\n ? String(workspace.activeTabId)\n : tabs[0]\n ? String(tabs[0].id)\n : null;\n\n return {\n activeTabId,\n tabs: tabs.map(serializeViewTab),\n };\n};\n\nconst readViewSection = async (db: ViewDbClient, id: number): Promise<ViewSectionRecord | null> => {\n const section = await db.viewSection.findUnique({\n where: { id },\n include: orderedViewSectionTagsInclude,\n });\n\n return section ? serializeViewSection(section) : null;\n};\n\nexport const hydratePropertyFilters = async (\n db: ViewDbClient,\n filters: ViewPropertyFilterRecord[],\n options: { validateSelectOptions?: boolean } = {},\n) => {\n if (filters.length === 0) {\n return [];\n }\n\n const definitions = await db.propertyDefinition.findMany({\n where: { key: { in: filters.map((filter) => filter.key) } },\n select: {\n key: true,\n name: true,\n valueType: true,\n options: {\n select: {\n value: true,\n },\n },\n },\n });\n const definitionByKey = new Map(definitions.map((definition) => [definition.key, definition]));\n\n return filters.map((filter) => {\n const definition = definitionByKey.get(filter.key);\n\n if (!definition) {\n throw new InvalidNotePropertyInputError(`Property ${filter.key} is not defined.`);\n }\n\n if (definition.valueType !== filter.valueType) {\n throw new InvalidNotePropertyInputError(`Property ${filter.key} uses ${definition.valueType} values.`);\n }\n\n if (\n options.validateSelectOptions &&\n filter.valueType === 'select' &&\n filter.operator !== 'exists' &&\n filter.operator !== 'notExists'\n ) {\n const optionValue = normalizeSelectFilterValue(filter.value ?? '');\n\n if (!definition.options.some((option) => option.value === optionValue)) {\n throw new InvalidNotePropertyInputError(`Property ${filter.key} option ${optionValue} is not defined.`);\n }\n }\n\n return {\n ...filter,\n name: definition.name,\n };\n });\n};\n\nconst buildStoredViewQuery = async (db: ViewDbClient, section: ReturnType<typeof normalizeViewSectionInput>) => {\n const propertyFilters = await hydratePropertyFilters(db, section.propertyFilters, { validateSelectOptions: true });\n\n return serializeStoredViewQuery({\n propertyFilters,\n sortBy: section.sortBy,\n sortOrder: section.sortOrder,\n displayOptions: section.displayOptions,\n });\n};\n\nconst getNextTabOrder = async (db: ViewDbClient) => {\n const lastTab = await db.viewTab.findFirst({\n orderBy: [{ order: 'desc' }, { createdAt: 'desc' }],\n select: { order: true },\n });\n\n return lastTab ? lastTab.order + 1 : 0;\n};\n\nconst getNextSectionOrder = async (db: ViewDbClient, tabId: number) => {\n const lastSection = await db.viewSection.findFirst({\n where: { tabId },\n orderBy: [{ order: 'desc' }, { createdAt: 'desc' }],\n select: { order: true },\n });\n\n return lastSection ? lastSection.order + 1 : 0;\n};\n\nconst resequenceViewTabs = async (db: ViewDbClient) => {\n const tabs = await db.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n\n await Promise.all(\n tabs.map((tab, index) =>\n db.viewTab.update({\n where: { id: tab.id },\n data: { order: index },\n }),\n ),\n );\n};\n\nconst resequenceViewSections = async (db: ViewDbClient, tabId: number) => {\n const sections = await db.viewSection.findMany({\n where: { tabId },\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n\n await Promise.all(\n sections.map((section, index) =>\n db.viewSection.update({\n where: { id: section.id },\n data: { order: index },\n }),\n ),\n );\n};\n\nexport const getViewWorkspace = async () => {\n return readViewWorkspace(models);\n};\n\nexport const getViewSectionById = async (id: string) => {\n return readViewSection(models, parseViewId(id));\n};\n\nconst normalizeSelectFilterValue = (value: string) => {\n return value.trim().toLowerCase().replace(/\\s+/g, '-');\n};\n\nconst buildPropertyFilterValueWhere = (filter: ViewPropertyFilterRecord): Prisma.NotePropertyWhereInput => {\n switch (filter.valueType) {\n case 'number': {\n const numberValue = Number(filter.value);\n\n if (filter.operator === 'before') {\n return { numberValue: { lt: numberValue } };\n }\n\n if (filter.operator === 'after') {\n return { numberValue: { gt: numberValue } };\n }\n\n if (filter.operator === 'notEquals') {\n return { numberValue: { not: numberValue } };\n }\n\n return { numberValue };\n }\n case 'date': {\n const dateValue = new Date(`${filter.value}T00:00:00.000Z`);\n\n if (filter.operator === 'before') {\n return { dateValue: { lt: dateValue } };\n }\n\n if (filter.operator === 'after') {\n return { dateValue: { gt: dateValue } };\n }\n\n if (filter.operator === 'notEquals') {\n return { dateValue: { not: dateValue } };\n }\n\n return { dateValue };\n }\n case 'boolean': {\n if (filter.operator === 'notEquals') {\n return { boolValue: { not: filter.value === 'true' } };\n }\n\n return { boolValue: filter.value === 'true' };\n }\n case 'select':\n if (filter.operator === 'notEquals') {\n return { option: { is: { value: { not: normalizeSelectFilterValue(filter.value ?? '') } } } };\n }\n\n return { option: { is: { value: normalizeSelectFilterValue(filter.value ?? '') } } };\n case 'url':\n case 'text':\n default: {\n const textValue = filter.value?.toLowerCase() ?? '';\n\n if (filter.operator === 'notEquals') {\n return { textValueNormalized: { not: textValue } };\n }\n\n if (filter.operator === 'contains') {\n return { textValueNormalized: { contains: textValue } };\n }\n\n if (filter.operator === 'notContains') {\n return { textValueNormalized: { not: { contains: textValue } } };\n }\n\n return { textValueNormalized: textValue };\n }\n }\n};\n\nexport const buildPropertyFilterWhere = (filter: ViewPropertyFilterRecord): Prisma.NoteWhereInput => {\n const definitionWhere = {\n definition: {\n is: {\n key: filter.key,\n },\n },\n } satisfies Prisma.NotePropertyWhereInput;\n\n if (filter.operator === 'notExists') {\n return {\n properties: {\n none: definitionWhere,\n },\n };\n }\n\n if (filter.operator === 'exists') {\n return {\n properties: {\n some: definitionWhere,\n },\n };\n }\n\n return {\n properties: {\n some: {\n ...definitionWhere,\n ...buildPropertyFilterValueWhere(filter),\n },\n },\n };\n};\n\nexport const buildViewNotesWhere = (\n query: Pick<ViewNotesQueryRecord, 'tagNames' | 'mode' | 'propertyFilters'>,\n): Prisma.NoteWhereInput => {\n const clauses: Prisma.NoteWhereInput[] = [];\n\n if (query.tagNames.length > 0) {\n clauses.push(buildNoteTagNamesWhere(query.tagNames, query.mode as NoteTagMatchMode));\n }\n\n for (const filter of query.propertyFilters) {\n clauses.push(buildPropertyFilterWhere(filter));\n }\n\n if (clauses.length === 0) {\n return {};\n }\n\n return { AND: clauses };\n};\n\nexport const buildViewSectionWhere = (section: ViewSectionRecord): Prisma.NoteWhereInput => {\n return buildViewNotesWhere(section);\n};\n\nconst buildViewNotesOrderBy = (\n query: Pick<ViewNotesQueryRecord, 'sortBy' | 'sortOrder'>,\n): Prisma.NoteOrderByWithRelationInput[] => {\n return [{ [query.sortBy]: query.sortOrder }, { id: 'asc' }];\n};\n\nconst buildViewSectionOrderBy = (section: ViewSectionRecord): Prisma.NoteOrderByWithRelationInput[] => {\n return buildViewNotesOrderBy(section);\n};\n\nexport const getViewSectionNotes = async (\n id: string,\n pagination: {\n limit: number;\n offset: number;\n },\n): Promise<ViewSectionNotesResult | null> => {\n const section = await models.viewSection.findUnique({\n where: { id: parseViewId(id) },\n include: orderedViewSectionTagsInclude,\n });\n\n if (!section) {\n return null;\n }\n\n const serializedSection = serializeViewSection(section);\n const where = buildViewSectionWhere(serializedSection);\n\n const [totalCount, notes] = await Promise.all([\n models.note.count({ where }),\n models.note.findMany({\n orderBy: buildViewSectionOrderBy(serializedSection),\n where,\n take: Number(pagination.limit),\n skip: Number(pagination.offset),\n }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n};\n\nexport const getNotesByPropertiesWithDb = async (\n db: ViewDbClient,\n input: ViewNotesQueryInput,\n pagination?: {\n limit?: number;\n offset?: number;\n },\n): Promise<ViewSectionNotesResult> => {\n const normalizedQuery = normalizeViewNotesQueryInput(input);\n\n if (normalizedQuery.propertyFilters.length === 0) {\n throw new InvalidNotePropertyInputError('At least one property filter is required.');\n }\n\n const query = {\n ...normalizedQuery,\n propertyFilters: await hydratePropertyFilters(db, normalizedQuery.propertyFilters, {\n validateSelectOptions: true,\n }),\n };\n const normalizedPagination = normalizeViewNotesPagination(pagination);\n const where = buildViewNotesWhere(query);\n\n const [totalCount, notes] = await Promise.all([\n db.note.count({ where }),\n db.note.findMany({\n orderBy: buildViewNotesOrderBy(query),\n where,\n take: normalizedPagination.limit,\n skip: normalizedPagination.offset,\n }),\n ]);\n\n return {\n totalCount,\n notes,\n };\n};\n\nexport const getNotesByProperties = async (\n input: ViewNotesQueryInput,\n pagination?: {\n limit?: number;\n offset?: number;\n },\n): Promise<ViewSectionNotesResult> => {\n return getNotesByPropertiesWithDb(models, input, pagination);\n};\n\nexport const createViewTab = async (title: string) => {\n return models.$transaction(async (tx) => {\n const workspace = await ensureViewWorkspace(tx);\n const nextTabOrder = await getNextTabOrder(tx);\n const createdTab = await tx.viewTab.create({\n data: {\n title: normalizeViewTabTitle(title),\n order: nextTabOrder,\n },\n include: orderedViewTabSectionsInclude,\n });\n\n if (workspace.activeTabId === null) {\n await tx.viewWorkspace.update({\n where: { id: VIEW_WORKSPACE_ID },\n data: { activeTabId: createdTab.id },\n });\n }\n\n return serializeViewTab(createdTab);\n });\n};\n\nexport const updateViewTab = async (id: string, title: string) => {\n const updatedTab = await models.viewTab.update({\n where: { id: parseViewId(id) },\n data: {\n title: normalizeViewTabTitle(title),\n },\n include: orderedViewTabSectionsInclude,\n });\n\n return serializeViewTab(updatedTab);\n};\n\nexport const deleteViewTab = async (id: string) => {\n const tabId = parseViewId(id);\n\n return models.$transaction(async (tx) => {\n const workspace = await ensureViewWorkspace(tx);\n const tabs = await tx.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n\n if (!tabs.some((tab) => tab.id === tabId)) {\n return false;\n }\n\n await tx.viewTab.delete({ where: { id: tabId } });\n await resequenceViewTabs(tx);\n\n const nextActiveTabId = pickNextActiveViewTabId(\n tabs.map((tab) => tab.id),\n tabId,\n workspace.activeTabId,\n );\n\n await tx.viewWorkspace.update({\n where: { id: VIEW_WORKSPACE_ID },\n data: { activeTabId: nextActiveTabId },\n });\n\n return true;\n });\n};\n\nexport const setActiveViewTab = async (id: string) => {\n const tabId = parseViewId(id);\n\n return models.$transaction(async (tx) => {\n const workspace = await ensureViewWorkspace(tx);\n const tab = await tx.viewTab.findUnique({\n where: { id: tabId },\n select: { id: true },\n });\n\n if (!tab) {\n throw new Error('View tab not found.');\n }\n\n await tx.viewWorkspace.update({\n where: { id: workspace.id },\n data: { activeTabId: tabId },\n });\n\n return readViewWorkspace(tx);\n });\n};\n\nexport const reorderViewTabs = async (tabIds: string[]) => {\n const nextTabIds = tabIds.map(parseViewId);\n\n return models.$transaction(async (tx) => {\n const tabs = await tx.viewTab.findMany({\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n const currentTabIds = tabs.map((tab) => tab.id);\n\n ensureValidReorderIds(currentTabIds, nextTabIds);\n\n await Promise.all(\n nextTabIds.map((tabId, index) =>\n tx.viewTab.update({\n where: { id: tabId },\n data: { order: index },\n }),\n ),\n );\n\n const reorderedTabs = await getOrderedViewTabs(tx);\n return reorderedTabs.map(serializeViewTab);\n });\n};\n\nexport const createViewSection = async (tabId: string, input: ViewSectionInput) => {\n const numericTabId = parseViewId(tabId);\n const nextSection = normalizeViewSectionInput(input);\n\n return models.$transaction(async (tx) => {\n const tab = await tx.viewTab.findUnique({\n where: { id: numericTabId },\n select: { id: true },\n });\n\n if (!tab) {\n throw new Error('View tab not found.');\n }\n\n const nextSectionOrder = await getNextSectionOrder(tx, numericTabId);\n const query = await buildStoredViewQuery(tx, nextSection);\n\n const createdSection = await tx.viewSection.create({\n data: {\n tabId: numericTabId,\n title: nextSection.title,\n displayType: nextSection.displayType,\n query,\n mode: nextSection.mode,\n limit: nextSection.limit,\n order: nextSectionOrder,\n tags: {\n create: nextSection.tagNames.map((tagName, index) => ({\n name: tagName,\n order: index,\n })),\n },\n },\n include: orderedViewSectionTagsInclude,\n });\n\n return serializeViewSection(createdSection);\n });\n};\n\nexport const updateViewSection = async (id: string, input: ViewSectionInput) => {\n const sectionId = parseViewId(id);\n const nextSection = normalizeViewSectionInput(input);\n\n return models.$transaction(async (tx) => {\n const query = await buildStoredViewQuery(tx, nextSection);\n\n const updatedSection = await tx.viewSection.update({\n where: { id: sectionId },\n data: {\n title: nextSection.title,\n displayType: nextSection.displayType,\n query,\n mode: nextSection.mode,\n limit: nextSection.limit,\n tags: {\n deleteMany: {},\n create: nextSection.tagNames.map((tagName, index) => ({\n name: tagName,\n order: index,\n })),\n },\n },\n include: orderedViewSectionTagsInclude,\n });\n\n return serializeViewSection(updatedSection);\n });\n};\n\nexport const deleteViewSection = async (id: string) => {\n const sectionId = parseViewId(id);\n\n return models.$transaction(async (tx) => {\n const section = await tx.viewSection.findUnique({\n where: { id: sectionId },\n select: { id: true, tabId: true },\n });\n\n if (!section) {\n return false;\n }\n\n await tx.viewSection.delete({\n where: { id: sectionId },\n });\n await resequenceViewSections(tx, section.tabId);\n\n return true;\n });\n};\n\nexport const reorderViewSections = async (tabId: string, sectionIds: string[]) => {\n const numericTabId = parseViewId(tabId);\n const nextSectionIds = sectionIds.map(parseViewId);\n\n return models.$transaction(async (tx) => {\n const sections = await tx.viewSection.findMany({\n where: { tabId: numericTabId },\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n select: { id: true },\n });\n const currentSectionIds = sections.map((section) => section.id);\n\n ensureValidReorderIds(currentSectionIds, nextSectionIds);\n\n await Promise.all(\n nextSectionIds.map((sectionId, index) =>\n tx.viewSection.update({\n where: { id: sectionId },\n data: { order: index },\n }),\n ),\n );\n\n const reorderedSections = await tx.viewSection.findMany({\n where: { tabId: numericTabId },\n orderBy: [{ order: 'asc' }, { createdAt: 'asc' }],\n include: orderedViewSectionTagsInclude,\n });\n\n return reorderedSections.map(serializeViewSection);\n });\n};\n"],"mappings":"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","names":[]}
|
|
@@ -175,6 +175,8 @@ const ANGLE_BRACKET_PLACEHOLDER_PREFIX = "\uE000OBANGLE";
|
|
|
175
175
|
const ANGLE_BRACKET_PLACEHOLDER_SUFFIX = "\uE001";
|
|
176
176
|
const NUMERIC_TILDE_RANGE_PLACEHOLDER_PREFIX = "\uE000OBTILDE";
|
|
177
177
|
const NUMERIC_TILDE_RANGE_PLACEHOLDER_SUFFIX = "\uE001";
|
|
178
|
+
const HARD_BREAK_PLACEHOLDER_PREFIX = "\uE000OBHARDBREAK";
|
|
179
|
+
const HARD_BREAK_PLACEHOLDER_SUFFIX = "\uE001";
|
|
178
180
|
function isMarkdownAutolinkAngleBracketText(innerText) {
|
|
179
181
|
if (innerText !== innerText.trim()) {
|
|
180
182
|
return false;
|
|
@@ -253,6 +255,49 @@ function protectMarkdownNumericTildeRanges(markdown) {
|
|
|
253
255
|
placeholderToToken
|
|
254
256
|
};
|
|
255
257
|
}
|
|
258
|
+
function protectMarkdownLineEndHardBreakMarkers(markdown) {
|
|
259
|
+
const protectedLines = [];
|
|
260
|
+
const placeholderToToken = /* @__PURE__ */ new Map();
|
|
261
|
+
let activeFence = null;
|
|
262
|
+
const lines = markdown.split(/(?<=\n)/);
|
|
263
|
+
for (const [index, line] of lines.entries()) {
|
|
264
|
+
const { body: lineBody, lineEnding } = splitMarkdownLineEnding(line);
|
|
265
|
+
const fenceMatch = lineBody.match(MARKDOWN_FENCED_CODE_PATTERN);
|
|
266
|
+
if (activeFence) {
|
|
267
|
+
protectedLines.push(line);
|
|
268
|
+
if (isClosingFenceLine(lineBody, activeFence)) {
|
|
269
|
+
activeFence = null;
|
|
270
|
+
}
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (fenceMatch?.[1]) {
|
|
274
|
+
const marker = fenceMatch[1][0];
|
|
275
|
+
activeFence = {
|
|
276
|
+
marker,
|
|
277
|
+
length: fenceMatch[1].length
|
|
278
|
+
};
|
|
279
|
+
protectedLines.push(line);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (/^(?: {4,}|\t)/.test(lineBody)) {
|
|
283
|
+
protectedLines.push(line);
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
const nextLineBody = splitMarkdownLineEnding(lines[index + 1] ?? "").body;
|
|
287
|
+
protectedLines.push(
|
|
288
|
+
`${protectMarkdownLineEndHardBreakMarker(
|
|
289
|
+
lineBody,
|
|
290
|
+
lineEnding,
|
|
291
|
+
isMarkdownHardBreakContinuationLine(nextLineBody),
|
|
292
|
+
placeholderToToken
|
|
293
|
+
)}${lineEnding}`
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
markdown: protectedLines.join(""),
|
|
298
|
+
placeholderToToken
|
|
299
|
+
};
|
|
300
|
+
}
|
|
256
301
|
function protectMarkdownTextAngleBrackets(markdown) {
|
|
257
302
|
const protectedLines = [];
|
|
258
303
|
const placeholderToToken = /* @__PURE__ */ new Map();
|
|
@@ -404,12 +449,61 @@ function replaceNumericTildeRangeMarkers(text, placeholderToToken) {
|
|
|
404
449
|
return `${leadingDigit}${placeholder}`;
|
|
405
450
|
});
|
|
406
451
|
}
|
|
452
|
+
function protectMarkdownLineEndHardBreakMarker(line, lineEnding, hasContinuationLine, placeholderToToken) {
|
|
453
|
+
if (!lineEnding || !line.endsWith("\\")) {
|
|
454
|
+
return line;
|
|
455
|
+
}
|
|
456
|
+
const placeholder = createHardBreakPlaceholder(placeholderToToken.size);
|
|
457
|
+
const shouldPreserveAsHardBreak = hasContinuationLine && !hasUnclosedInlineCodeSpanAtLineEnd(line);
|
|
458
|
+
placeholderToToken.set(placeholder, shouldPreserveAsHardBreak ? "" : "\\");
|
|
459
|
+
return `${line.slice(0, -1)}${placeholder}`;
|
|
460
|
+
}
|
|
461
|
+
function isMarkdownHardBreakContinuationLine(line) {
|
|
462
|
+
if (!line.trim()) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
const trimmedLine = line.trimStart();
|
|
466
|
+
if (trimmedLine.match(MARKDOWN_FENCED_CODE_PATTERN)) {
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
if (/^(?:#{1,6})(?:\s|$)/.test(trimmedLine)) {
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
472
|
+
if (/^>/.test(trimmedLine)) {
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
if (/^(?:[-+*])(?:\s|$)/.test(trimmedLine) || /^\d{1,9}[.)](?:\s|$)/.test(trimmedLine)) {
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
if (/^(?:-{3,}|\*{3,}|_{3,})\s*$/.test(trimmedLine)) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
function hasUnclosedInlineCodeSpanAtLineEnd(line) {
|
|
484
|
+
let cursor = 0;
|
|
485
|
+
let openDelimiter = null;
|
|
486
|
+
while (cursor < line.length) {
|
|
487
|
+
const codeStart = line.indexOf("`", cursor);
|
|
488
|
+
if (codeStart === -1) {
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
const delimiterLength = countRepeatedCharacter(line, codeStart, "`");
|
|
492
|
+
const delimiter = "`".repeat(delimiterLength);
|
|
493
|
+
openDelimiter = openDelimiter === delimiter ? null : delimiter;
|
|
494
|
+
cursor = codeStart + delimiterLength;
|
|
495
|
+
}
|
|
496
|
+
return openDelimiter !== null;
|
|
497
|
+
}
|
|
407
498
|
function createAngleBracketPlaceholder(index) {
|
|
408
499
|
return `${ANGLE_BRACKET_PLACEHOLDER_PREFIX}${index}${ANGLE_BRACKET_PLACEHOLDER_SUFFIX}`;
|
|
409
500
|
}
|
|
410
501
|
function createNumericTildeRangePlaceholder(index) {
|
|
411
502
|
return `${NUMERIC_TILDE_RANGE_PLACEHOLDER_PREFIX}${index}${NUMERIC_TILDE_RANGE_PLACEHOLDER_SUFFIX}`;
|
|
412
503
|
}
|
|
504
|
+
function createHardBreakPlaceholder(index) {
|
|
505
|
+
return `${HARD_BREAK_PLACEHOLDER_PREFIX}${index}${HARD_BREAK_PLACEHOLDER_SUFFIX}`;
|
|
506
|
+
}
|
|
413
507
|
function countRepeatedCharacter(value, start, character) {
|
|
414
508
|
let cursor = start;
|
|
415
509
|
while (value[cursor] === character) {
|
|
@@ -614,12 +708,17 @@ async function markdownToBlocksJson(markdown, deps = defaultMarkdownImportDeps)
|
|
|
614
708
|
const editor = getEditor();
|
|
615
709
|
const preprocessedMarkdown = preprocessMarkdownExplicitTags(markdown);
|
|
616
710
|
const tildeProtectedMarkdown = protectMarkdownNumericTildeRanges(preprocessedMarkdown.markdown);
|
|
711
|
+
const hardBreakProtectedMarkdown = protectMarkdownLineEndHardBreakMarkers(tildeProtectedMarkdown.markdown);
|
|
712
|
+
const basePlaceholderToToken = new Map([
|
|
713
|
+
...tildeProtectedMarkdown.placeholderToToken,
|
|
714
|
+
...hardBreakProtectedMarkdown.placeholderToToken
|
|
715
|
+
]);
|
|
617
716
|
const contentJson = await parseMarkdownToContentJson(
|
|
618
717
|
editor,
|
|
619
|
-
|
|
718
|
+
hardBreakProtectedMarkdown.markdown,
|
|
620
719
|
deps,
|
|
621
720
|
preprocessedMarkdown.placeholderToTag,
|
|
622
|
-
|
|
721
|
+
basePlaceholderToToken
|
|
623
722
|
);
|
|
624
723
|
if (extractLiteralAngleBracketTextTokens(preprocessedMarkdown.markdown).length === 0) {
|
|
625
724
|
return contentJson;
|
|
@@ -627,11 +726,8 @@ async function markdownToBlocksJson(markdown, deps = defaultMarkdownImportDeps)
|
|
|
627
726
|
if (!hasLiteralAngleBracketTextTokenLoss(preprocessedMarkdown.markdown, await blocksToMarkdown(contentJson))) {
|
|
628
727
|
return contentJson;
|
|
629
728
|
}
|
|
630
|
-
const protectedMarkdown = protectMarkdownTextAngleBrackets(
|
|
631
|
-
const placeholderToToken = new Map([
|
|
632
|
-
...tildeProtectedMarkdown.placeholderToToken,
|
|
633
|
-
...protectedMarkdown.placeholderToToken
|
|
634
|
-
]);
|
|
729
|
+
const protectedMarkdown = protectMarkdownTextAngleBrackets(hardBreakProtectedMarkdown.markdown);
|
|
730
|
+
const placeholderToToken = new Map([...basePlaceholderToToken, ...protectedMarkdown.placeholderToToken]);
|
|
635
731
|
return parseMarkdownToContentJson(
|
|
636
732
|
editor,
|
|
637
733
|
protectedMarkdown.markdown,
|