ocean-brain 0.4.2 → 0.6.0
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/dist/mcp.js +660 -31
- package/package.json +2 -1
- package/server/client/dist/assets/{Calendar-DI0PhQTq.js → Calendar-BytW4PeT.js} +1 -1
- package/server/client/dist/assets/Callout-9sJgGNRb.js +1 -0
- package/server/client/dist/assets/Graph-o5u-NzrA.js +1 -0
- package/server/client/dist/assets/{Image-DlJ4MDo3.js → Image-DxWhlIDG.js} +1 -1
- package/server/client/dist/assets/{Image.es-COwqkQuh.js → Image.es-B0LL3pb7.js} +1 -1
- package/server/client/dist/assets/Note-CYoYzZNu.js +21 -0
- package/server/client/dist/assets/{Plus.es-KeV96R_e.js → Plus.es-DpxzgRtY.js} +1 -1
- package/server/client/dist/assets/Reminders-DAepTZg1.js +1 -0
- package/server/client/dist/assets/Search-6KH7An44.js +1 -0
- package/server/client/dist/assets/{SurfaceCard-CHjcCuqZ.js → SurfaceCard-EMs50xSF.js} +1 -1
- package/server/client/dist/assets/Tag-C89j8x56.js +1 -0
- package/server/client/dist/assets/TagNotes-BTbu-f6v.js +1 -0
- package/server/client/dist/assets/{Trash.es-BqRTZPrI.js → Trash.es-C5x35NKF.js} +1 -1
- package/server/client/dist/assets/ViewNotes-DPEuki3y.js +1 -0
- package/server/client/dist/assets/Views-BGkuNhVd.js +1 -0
- package/server/client/dist/assets/{image.api-CMdQbCyN.js → image.api-DRhRQ0Gf.js} +1 -1
- package/server/client/dist/assets/{index-D2gRB1ig.css → index-Cd81UxEh.css} +1 -1
- package/server/client/dist/assets/index-DYlYUrjH.js +327 -0
- package/server/client/dist/assets/index-DswSFFMb.js +1 -0
- package/server/client/dist/assets/manage-image-C6sHhWnI.js +1 -0
- package/server/client/dist/assets/manage-image-detail-CJkfybR9.js +1 -0
- package/server/client/dist/assets/{mcp-DqGLZv90.js → mcp-piouFDNm.js} +1 -1
- package/server/client/dist/assets/note-core-Z6kqulGB.js +180 -0
- package/server/client/dist/assets/note-vendor-i1M2FSI2.js +54 -0
- package/server/client/dist/assets/{placeholder-BXLtvdtN.js → placeholder-DGz3CHej.js} +4 -4
- package/server/client/dist/assets/properties-CM4khczg.js +1 -0
- package/server/client/dist/assets/trash-C2lItz-b.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-v1EXqM90.js → useReminderMutate-DHjDDHKg.js} +1 -1
- package/server/client/dist/assets/view-dashboard-C17WsdL7.js +99 -0
- package/server/client/dist/index.html +4 -4
- package/server/dist/features/note/graphql/note.field.resolver.js +16 -1
- package/server/dist/features/note/graphql/note.field.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js +192 -36
- package/server/dist/features/note/graphql/note.mutation.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.query.resolver.js +88 -76
- package/server/dist/features/note/graphql/note.query.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.resolver.js +3 -2
- package/server/dist/features/note/graphql/note.resolver.js.map +1 -1
- package/server/dist/features/note/graphql/note.type-defs.js +104 -1
- package/server/dist/features/note/graphql/note.type-defs.js.map +1 -1
- package/server/dist/features/note/http/mcp.js +394 -1
- package/server/dist/features/note/http/mcp.js.map +1 -1
- package/server/dist/features/note/services/cleanup.js +4 -3
- package/server/dist/features/note/services/cleanup.js.map +1 -1
- package/server/dist/features/note/services/content-blocks.js +113 -0
- package/server/dist/features/note/services/content-blocks.js.map +1 -0
- package/server/dist/features/note/services/markdown-intent-write.js +478 -0
- package/server/dist/features/note/services/markdown-intent-write.js.map +1 -0
- package/server/dist/features/note/services/markdown-patch.js +601 -0
- package/server/dist/features/note/services/markdown-patch.js.map +1 -0
- package/server/dist/features/note/services/properties.js +874 -0
- package/server/dist/features/note/services/properties.js.map +1 -0
- package/server/dist/features/note/services/search.js +10 -29
- package/server/dist/features/note/services/search.js.map +1 -1
- package/server/dist/features/note/services/snapshot.js +260 -18
- package/server/dist/features/note/services/snapshot.js.map +1 -1
- package/server/dist/features/note/services/trash.js +70 -2
- package/server/dist/features/note/services/trash.js.map +1 -1
- package/server/dist/features/note/services/write-conflict.js +87 -0
- package/server/dist/features/note/services/write-conflict.js.map +1 -0
- package/server/dist/features/note/services/write.js +121 -0
- package/server/dist/features/note/services/write.js.map +1 -0
- package/server/dist/features/view/graphql/view.query.resolver.js +31 -1
- package/server/dist/features/view/graphql/view.query.resolver.js.map +1 -1
- package/server/dist/features/view/graphql/view.type-defs.js +57 -1
- package/server/dist/features/view/graphql/view.type-defs.js.map +1 -1
- package/server/dist/features/view/services/workspace.js +367 -43
- package/server/dist/features/view/services/workspace.js.map +1 -1
- package/server/dist/modules/blocknote-tree.js +41 -0
- package/server/dist/modules/blocknote-tree.js.map +1 -0
- package/server/dist/modules/blocknote.js +47 -2
- package/server/dist/modules/blocknote.js.map +1 -1
- package/server/dist/modules/error-handler.js +7 -4
- package/server/dist/modules/error-handler.js.map +1 -1
- package/server/dist/modules/recovery-retention.js +2 -2
- package/server/dist/modules/recovery-retention.js.map +1 -1
- package/server/dist/routes/mcp.js +21 -1
- package/server/dist/routes/mcp.js.map +1 -1
- package/server/prisma/migrations/20260527073000_0017_note_properties/migration.sql +105 -0
- package/server/prisma/migrations/20260531073000_0018_view_query/migration.sql +3 -0
- package/server/prisma/schema.prisma +226 -130
- package/server/client/dist/assets/Callout-BAqbXI0L.js +0 -1
- package/server/client/dist/assets/Graph-Dhuog4tc.js +0 -1
- package/server/client/dist/assets/Note-ChUWU3Su.js +0 -21
- package/server/client/dist/assets/Reminders-D7MCTNxb.js +0 -1
- package/server/client/dist/assets/Search-B-fwTr5y.js +0 -1
- package/server/client/dist/assets/Tag-BbV2z-Db.js +0 -1
- package/server/client/dist/assets/TagNotes-2HKF09Jm.js +0 -1
- package/server/client/dist/assets/ViewNotes-TfM3Uzbp.js +0 -1
- package/server/client/dist/assets/Views-CUwtnfa8.js +0 -1
- package/server/client/dist/assets/index-B4l2UvuC.js +0 -232
- package/server/client/dist/assets/index-zhxZjg7V.js +0 -1
- package/server/client/dist/assets/manage-image-DdXb5Bkj.js +0 -1
- package/server/client/dist/assets/manage-image-detail-CrrXCQIX.js +0 -1
- package/server/client/dist/assets/note-core-GgFcksAF.js +0 -178
- package/server/client/dist/assets/note-vendor-B0t5F04o.js +0 -54
- package/server/client/dist/assets/trash-3eUAjFRh.js +0 -1
- package/server/client/dist/assets/view-dashboard-T6gmohr-.js +0 -90
- package/server/dist/features/note/graphql/note.graphql.shared.js +0 -23
- package/server/dist/features/note/graphql/note.graphql.shared.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/view/graphql/view.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { getViewSectionById
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/view/graphql/view.query.resolver.ts"],"sourcesContent":["import type { IResolvers } from '@graphql-tools/utils';\nimport { GraphQLError } from 'graphql';\nimport { InvalidNotePropertyInputError } from '~/features/note/services/properties.js';\nimport {\n getNotesByProperties,\n getViewSectionById,\n getViewSectionNotes,\n getViewWorkspace,\n type ViewNotesQueryInput,\n} from '~/features/view/services/workspace.js';\nimport type { Pagination } from '~/types/index.js';\n\ntype ViewQueryResolvers = NonNullable<IResolvers['Query']>;\n\nexport const viewQueryResolvers: ViewQueryResolvers = {\n viewWorkspace: async () => {\n return getViewWorkspace();\n },\n viewSection: async (_, { id }: { id: string }) => {\n return getViewSectionById(id);\n },\n viewSectionNotes: async (\n _,\n {\n id,\n pagination = {\n limit: 25,\n offset: 0,\n },\n }: {\n id: string;\n pagination: Pagination;\n },\n ) => {\n const sectionNotes = await getViewSectionNotes(id, {\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n\n if (!sectionNotes) {\n throw 'NOT FOUND';\n }\n\n return sectionNotes;\n },\n notesByProperties: async (\n _,\n {\n input,\n pagination = {\n limit: 20,\n offset: 0,\n },\n }: {\n input: ViewNotesQueryInput;\n pagination: Pagination;\n },\n ) => {\n try {\n return await getNotesByProperties(input, {\n limit: Number(pagination.limit),\n offset: Number(pagination.offset),\n });\n } catch (error) {\n if (error instanceof InvalidNotePropertyInputError) {\n throw new GraphQLError(error.message, {\n extensions: {\n code: 'INVALID_NOTE_PROPERTY_INPUT',\n },\n });\n }\n\n throw error;\n }\n },\n};\n"],"mappings":"AACA,SAAS,oBAAoB;AAC7B,SAAS,qCAAqC;AAC9C;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEG;AAKA,MAAM,qBAAyC;AAAA,EAClD,eAAe,YAAY;AACvB,WAAO,iBAAiB;AAAA,EAC5B;AAAA,EACA,aAAa,OAAO,GAAG,EAAE,GAAG,MAAsB;AAC9C,WAAO,mBAAmB,EAAE;AAAA,EAChC;AAAA,EACA,kBAAkB,OACd,GACA;AAAA,IACI;AAAA,IACA,aAAa;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,IACZ;AAAA,EACJ,MAIC;AACD,UAAM,eAAe,MAAM,oBAAoB,IAAI;AAAA,MAC/C,OAAO,OAAO,WAAW,KAAK;AAAA,MAC9B,QAAQ,OAAO,WAAW,MAAM;AAAA,IACpC,CAAC;AAED,QAAI,CAAC,cAAc;AACf,YAAM;AAAA,IACV;AAEA,WAAO;AAAA,EACX;AAAA,EACA,mBAAmB,OACf,GACA;AAAA,IACI;AAAA,IACA,aAAa;AAAA,MACT,OAAO;AAAA,MACP,QAAQ;AAAA,IACZ;AAAA,EACJ,MAIC;AACD,QAAI;AACA,aAAO,MAAM,qBAAqB,OAAO;AAAA,QACrC,OAAO,OAAO,WAAW,KAAK;AAAA,QAC9B,QAAQ,OAAO,WAAW,MAAM;AAAA,MACpC,CAAC;AAAA,IACL,SAAS,OAAO;AACZ,UAAI,iBAAiB,+BAA+B;AAChD,cAAM,IAAI,aAAa,MAAM,SAAS;AAAA,UAClC,YAAY;AAAA,YACR,MAAM;AAAA,UACV;AAAA,QACJ,CAAC;AAAA,MACL;AAEA,YAAM;AAAA,IACV;AAAA,EACJ;AACJ;","names":[]}
|
|
@@ -16,24 +16,80 @@ const viewType = gql`
|
|
|
16
16
|
id: ID!
|
|
17
17
|
tabId: ID!
|
|
18
18
|
title: String!
|
|
19
|
+
displayType: ViewDisplayType!
|
|
19
20
|
tagNames: [String!]!
|
|
20
21
|
mode: TagMatchMode!
|
|
22
|
+
propertyFilters: [ViewPropertyFilter!]!
|
|
23
|
+
sortBy: ViewSortBy!
|
|
24
|
+
sortOrder: ViewSortOrder!
|
|
21
25
|
limit: Int!
|
|
22
26
|
order: Int!
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
type ViewPropertyFilter {
|
|
30
|
+
key: String!
|
|
31
|
+
name: String!
|
|
32
|
+
valueType: NotePropertyValueType!
|
|
33
|
+
operator: ViewPropertyFilterOperator!
|
|
34
|
+
value: String
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
enum ViewDisplayType {
|
|
38
|
+
list
|
|
39
|
+
calendar
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
enum ViewPropertyFilterOperator {
|
|
43
|
+
equals
|
|
44
|
+
before
|
|
45
|
+
after
|
|
46
|
+
exists
|
|
47
|
+
notExists
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
enum ViewSortBy {
|
|
51
|
+
updatedAt
|
|
52
|
+
createdAt
|
|
53
|
+
title
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
enum ViewSortOrder {
|
|
57
|
+
asc
|
|
58
|
+
desc
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
input ViewPropertyFilterInput {
|
|
62
|
+
key: String!
|
|
63
|
+
valueType: NotePropertyValueType!
|
|
64
|
+
operator: ViewPropertyFilterOperator!
|
|
65
|
+
value: String
|
|
66
|
+
}
|
|
67
|
+
|
|
25
68
|
input ViewSectionInput {
|
|
26
69
|
title: String
|
|
27
|
-
|
|
70
|
+
displayType: ViewDisplayType
|
|
71
|
+
tagNames: [String!]
|
|
28
72
|
mode: TagMatchMode
|
|
73
|
+
propertyFilters: [ViewPropertyFilterInput!]
|
|
74
|
+
sortBy: ViewSortBy
|
|
75
|
+
sortOrder: ViewSortOrder
|
|
29
76
|
limit: Int
|
|
30
77
|
}
|
|
78
|
+
|
|
79
|
+
input NotesByPropertiesInput {
|
|
80
|
+
tagNames: [String!]
|
|
81
|
+
mode: TagMatchMode
|
|
82
|
+
propertyFilters: [ViewPropertyFilterInput!]!
|
|
83
|
+
sortBy: ViewSortBy
|
|
84
|
+
sortOrder: ViewSortOrder
|
|
85
|
+
}
|
|
31
86
|
`;
|
|
32
87
|
const viewQuery = gql`
|
|
33
88
|
type Query {
|
|
34
89
|
viewWorkspace: ViewWorkspace!
|
|
35
90
|
viewSection(id: ID!): ViewSection
|
|
36
91
|
viewSectionNotes(id: ID!, pagination: PaginationInput): Notes!
|
|
92
|
+
notesByProperties(input: NotesByPropertiesInput!, pagination: PaginationInput): Notes!
|
|
37
93
|
}
|
|
38
94
|
`;
|
|
39
95
|
const viewMutation = gql`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/features/view/graphql/view.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const viewType = gql`\n type ViewWorkspace {\n activeTabId: ID\n tabs: [ViewTab!]!\n }\n\n type ViewTab {\n id: ID!\n title: String!\n order: Int!\n sections: [ViewSection!]!\n }\n\n type ViewSection {\n id: ID!\n tabId: ID!\n title: String!\n tagNames: [String!]!\n mode: TagMatchMode!\n limit: Int!\n order: Int!\n }\n\n input ViewSectionInput {\n title: String\n tagNames: [String!]
|
|
1
|
+
{"version":3,"sources":["../../../../src/features/view/graphql/view.type-defs.ts"],"sourcesContent":["import { gql } from '~/modules/graphql.js';\n\nexport const viewType = gql`\n type ViewWorkspace {\n activeTabId: ID\n tabs: [ViewTab!]!\n }\n\n type ViewTab {\n id: ID!\n title: String!\n order: Int!\n sections: [ViewSection!]!\n }\n\n type ViewSection {\n id: ID!\n tabId: ID!\n title: String!\n displayType: ViewDisplayType!\n tagNames: [String!]!\n mode: TagMatchMode!\n propertyFilters: [ViewPropertyFilter!]!\n sortBy: ViewSortBy!\n sortOrder: ViewSortOrder!\n limit: Int!\n order: Int!\n }\n\n type ViewPropertyFilter {\n key: String!\n name: String!\n valueType: NotePropertyValueType!\n operator: ViewPropertyFilterOperator!\n value: String\n }\n\n enum ViewDisplayType {\n list\n calendar\n }\n\n enum ViewPropertyFilterOperator {\n equals\n before\n after\n exists\n notExists\n }\n\n enum ViewSortBy {\n updatedAt\n createdAt\n title\n }\n\n enum ViewSortOrder {\n asc\n desc\n }\n\n input ViewPropertyFilterInput {\n key: String!\n valueType: NotePropertyValueType!\n operator: ViewPropertyFilterOperator!\n value: String\n }\n\n input ViewSectionInput {\n title: String\n displayType: ViewDisplayType\n tagNames: [String!]\n mode: TagMatchMode\n propertyFilters: [ViewPropertyFilterInput!]\n sortBy: ViewSortBy\n sortOrder: ViewSortOrder\n limit: Int\n }\n\n input NotesByPropertiesInput {\n tagNames: [String!]\n mode: TagMatchMode\n propertyFilters: [ViewPropertyFilterInput!]!\n sortBy: ViewSortBy\n sortOrder: ViewSortOrder\n }\n`;\n\nexport const viewQuery = gql`\n type Query {\n viewWorkspace: ViewWorkspace!\n viewSection(id: ID!): ViewSection\n viewSectionNotes(id: ID!, pagination: PaginationInput): Notes!\n notesByProperties(input: NotesByPropertiesInput!, pagination: PaginationInput): Notes!\n }\n`;\n\nexport const viewMutation = gql`\n type Mutation {\n createViewTab(title: String!): ViewTab!\n updateViewTab(id: ID!, title: String!): ViewTab!\n deleteViewTab(id: ID!): Boolean!\n setActiveViewTab(id: ID!): ViewWorkspace!\n reorderViewTabs(tabIds: [ID!]!): [ViewTab!]!\n createViewSection(tabId: ID!, input: ViewSectionInput!): ViewSection!\n updateViewSection(id: ID!, input: ViewSectionInput!): ViewSection!\n deleteViewSection(id: ID!): Boolean!\n reorderViewSections(tabId: ID!, sectionIds: [ID!]!): [ViewSection!]!\n }\n`;\n\nexport const viewTypeDefs = `\n ${viewType}\n ${viewQuery}\n ${viewMutation}\n`;\n"],"mappings":"AAAA,SAAS,WAAW;AAEb,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAsFjB,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASlB,MAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAcrB,MAAM,eAAe;AAAA,MACtB,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,YAAY;AAAA;","names":[]}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InvalidNotePropertyInputError,
|
|
3
|
+
normalizePropertyKey,
|
|
4
|
+
normalizeUrlValue
|
|
5
|
+
} from "../../../features/note/services/properties.js";
|
|
1
6
|
import { buildNoteTagNamesWhere } from "../../../features/note/services/tag-filter.js";
|
|
2
7
|
import models from "../../../models.js";
|
|
3
8
|
const VIEW_WORKSPACE_ID = 1;
|
|
4
9
|
const DEFAULT_VIEW_SECTION_LIMIT = 5;
|
|
5
10
|
const MIN_VIEW_SECTION_LIMIT = 1;
|
|
6
11
|
const MAX_VIEW_SECTION_LIMIT = 20;
|
|
12
|
+
const DEFAULT_VIEW_NOTES_QUERY_LIMIT = 20;
|
|
13
|
+
const MAX_VIEW_NOTES_QUERY_LIMIT = 50;
|
|
14
|
+
const MAX_VIEW_PROPERTY_FILTERS = 10;
|
|
15
|
+
const DEFAULT_VIEW_DISPLAY_TYPE = "list";
|
|
16
|
+
const DEFAULT_VIEW_SORT_BY = "updatedAt";
|
|
17
|
+
const DEFAULT_VIEW_SORT_ORDER = "desc";
|
|
7
18
|
const orderedViewSectionTagsInclude = {
|
|
8
19
|
tags: {
|
|
9
20
|
orderBy: [{ order: "asc" }, { createdAt: "asc" }]
|
|
@@ -32,6 +43,116 @@ const normalizeViewTagNames = (values) => {
|
|
|
32
43
|
const normalizedTagNames = values.flatMap((value) => value.split(","));
|
|
33
44
|
return Array.from(new Set(normalizedTagNames.map(normalizeTagName).filter(Boolean)));
|
|
34
45
|
};
|
|
46
|
+
const isViewPropertyFilterOperator = (value) => {
|
|
47
|
+
return value === "equals" || value === "before" || value === "after" || value === "exists" || value === "notExists";
|
|
48
|
+
};
|
|
49
|
+
const isPropertyValueType = (value) => {
|
|
50
|
+
return value === "text" || value === "url" || value === "number" || value === "date" || value === "boolean" || value === "select";
|
|
51
|
+
};
|
|
52
|
+
const normalizeViewDisplayType = (value) => {
|
|
53
|
+
return value === "calendar" ? "calendar" : DEFAULT_VIEW_DISPLAY_TYPE;
|
|
54
|
+
};
|
|
55
|
+
const normalizeViewSortBy = (value) => {
|
|
56
|
+
return value === "createdAt" || value === "title" ? value : DEFAULT_VIEW_SORT_BY;
|
|
57
|
+
};
|
|
58
|
+
const normalizeViewSortOrder = (value) => {
|
|
59
|
+
return value === "asc" ? "asc" : DEFAULT_VIEW_SORT_ORDER;
|
|
60
|
+
};
|
|
61
|
+
const normalizeFilterValue = ({
|
|
62
|
+
value,
|
|
63
|
+
valueType,
|
|
64
|
+
operator
|
|
65
|
+
}) => {
|
|
66
|
+
if (operator === "exists" || operator === "notExists") {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const normalizedValue = String(value ?? "").trim();
|
|
70
|
+
if (!normalizedValue) {
|
|
71
|
+
throw new InvalidNotePropertyInputError("Property filter value is required.");
|
|
72
|
+
}
|
|
73
|
+
if (valueType === "number" && !Number.isFinite(Number(normalizedValue))) {
|
|
74
|
+
throw new InvalidNotePropertyInputError("Number property filter value must be finite.");
|
|
75
|
+
}
|
|
76
|
+
if (valueType === "boolean" && normalizedValue !== "true" && normalizedValue !== "false") {
|
|
77
|
+
throw new InvalidNotePropertyInputError("Boolean property filter value must be true or false.");
|
|
78
|
+
}
|
|
79
|
+
if (valueType === "date") {
|
|
80
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(normalizedValue)) {
|
|
81
|
+
throw new InvalidNotePropertyInputError("Date property filter values must use YYYY-MM-DD.");
|
|
82
|
+
}
|
|
83
|
+
const date = /* @__PURE__ */ new Date(`${normalizedValue}T00:00:00.000Z`);
|
|
84
|
+
if (Number.isNaN(date.getTime()) || date.toISOString().slice(0, 10) !== normalizedValue) {
|
|
85
|
+
throw new InvalidNotePropertyInputError("Date property filter value is invalid.");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if ((operator === "before" || operator === "after") && valueType !== "date" && valueType !== "number") {
|
|
89
|
+
throw new InvalidNotePropertyInputError("Before and after filters require date or number properties.");
|
|
90
|
+
}
|
|
91
|
+
if (valueType === "url") {
|
|
92
|
+
return normalizeUrlValue(normalizedValue);
|
|
93
|
+
}
|
|
94
|
+
return normalizedValue;
|
|
95
|
+
};
|
|
96
|
+
const normalizeViewPropertyFilters = (filters) => {
|
|
97
|
+
const normalizedFilters = filters ?? [];
|
|
98
|
+
if (normalizedFilters.length > MAX_VIEW_PROPERTY_FILTERS) {
|
|
99
|
+
throw new InvalidNotePropertyInputError(`A view can have up to ${MAX_VIEW_PROPERTY_FILTERS} property filters.`);
|
|
100
|
+
}
|
|
101
|
+
return normalizedFilters.map((filter) => {
|
|
102
|
+
const key = normalizePropertyKey(filter.key);
|
|
103
|
+
const valueType = filter.valueType;
|
|
104
|
+
const operator = filter.operator;
|
|
105
|
+
if (!isPropertyValueType(valueType)) {
|
|
106
|
+
throw new InvalidNotePropertyInputError("Property filter value type is required.");
|
|
107
|
+
}
|
|
108
|
+
if (!isViewPropertyFilterOperator(operator)) {
|
|
109
|
+
throw new InvalidNotePropertyInputError("Property filter operator is invalid.");
|
|
110
|
+
}
|
|
111
|
+
const storedName = filter.name;
|
|
112
|
+
const name = typeof storedName === "string" && storedName.trim() ? storedName.trim() : key;
|
|
113
|
+
return {
|
|
114
|
+
key,
|
|
115
|
+
name,
|
|
116
|
+
valueType,
|
|
117
|
+
operator,
|
|
118
|
+
value: normalizeFilterValue({
|
|
119
|
+
value: filter.value,
|
|
120
|
+
valueType,
|
|
121
|
+
operator
|
|
122
|
+
})
|
|
123
|
+
};
|
|
124
|
+
}).filter((filter) => filter !== null);
|
|
125
|
+
};
|
|
126
|
+
const parseStoredViewQuery = (value) => {
|
|
127
|
+
if (!value) {
|
|
128
|
+
return {
|
|
129
|
+
propertyFilters: [],
|
|
130
|
+
sortBy: DEFAULT_VIEW_SORT_BY,
|
|
131
|
+
sortOrder: DEFAULT_VIEW_SORT_ORDER
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
const parsed = JSON.parse(value);
|
|
136
|
+
return {
|
|
137
|
+
propertyFilters: normalizeViewPropertyFilters(parsed.propertyFilters ?? []),
|
|
138
|
+
sortBy: normalizeViewSortBy(parsed.sortBy),
|
|
139
|
+
sortOrder: normalizeViewSortOrder(parsed.sortOrder)
|
|
140
|
+
};
|
|
141
|
+
} catch {
|
|
142
|
+
return {
|
|
143
|
+
propertyFilters: [],
|
|
144
|
+
sortBy: DEFAULT_VIEW_SORT_BY,
|
|
145
|
+
sortOrder: DEFAULT_VIEW_SORT_ORDER
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const serializeStoredViewQuery = ({ propertyFilters, sortBy, sortOrder }) => {
|
|
150
|
+
return JSON.stringify({
|
|
151
|
+
propertyFilters,
|
|
152
|
+
sortBy,
|
|
153
|
+
sortOrder
|
|
154
|
+
});
|
|
155
|
+
};
|
|
35
156
|
const clampViewSectionLimit = (value) => {
|
|
36
157
|
const numericValue = typeof value === "number" ? value : Number.NaN;
|
|
37
158
|
if (!Number.isInteger(numericValue)) {
|
|
@@ -43,25 +164,48 @@ const normalizeViewTabTitle = (title) => {
|
|
|
43
164
|
const trimmedTitle = title.trim();
|
|
44
165
|
return trimmedTitle || "Untitled View";
|
|
45
166
|
};
|
|
46
|
-
const buildDefaultSectionTitle = (tagNames) => {
|
|
47
|
-
if (tagNames.length
|
|
48
|
-
return "
|
|
167
|
+
const buildDefaultSectionTitle = (tagNames, propertyFilters) => {
|
|
168
|
+
if (tagNames.length > 0) {
|
|
169
|
+
return tagNames.slice(0, 2).join(" + ");
|
|
49
170
|
}
|
|
50
|
-
|
|
171
|
+
if (propertyFilters.length > 0) {
|
|
172
|
+
return propertyFilters.slice(0, 2).map((filter) => filter.name).join(" + ");
|
|
173
|
+
}
|
|
174
|
+
return "All notes";
|
|
51
175
|
};
|
|
52
176
|
const normalizeViewSectionInput = (input) => {
|
|
53
|
-
const tagNames = normalizeViewTagNames(input.tagNames);
|
|
54
|
-
|
|
55
|
-
throw new Error("A view section requires at least one tag.");
|
|
56
|
-
}
|
|
177
|
+
const tagNames = normalizeViewTagNames(input.tagNames ?? []);
|
|
178
|
+
const propertyFilters = normalizeViewPropertyFilters(input.propertyFilters);
|
|
57
179
|
const trimmedTitle = input.title?.trim() ?? "";
|
|
180
|
+
const sortBy = normalizeViewSortBy(input.sortBy);
|
|
181
|
+
const sortOrder = normalizeViewSortOrder(input.sortOrder);
|
|
58
182
|
return {
|
|
59
|
-
title: trimmedTitle || buildDefaultSectionTitle(tagNames),
|
|
183
|
+
title: trimmedTitle || buildDefaultSectionTitle(tagNames, propertyFilters),
|
|
184
|
+
displayType: normalizeViewDisplayType(input.displayType),
|
|
60
185
|
tagNames,
|
|
61
186
|
mode: input.mode === "or" ? "or" : "and",
|
|
187
|
+
propertyFilters,
|
|
188
|
+
sortBy,
|
|
189
|
+
sortOrder,
|
|
62
190
|
limit: clampViewSectionLimit(input.limit)
|
|
63
191
|
};
|
|
64
192
|
};
|
|
193
|
+
const normalizeViewNotesQueryInput = (input) => {
|
|
194
|
+
return {
|
|
195
|
+
tagNames: normalizeViewTagNames(input.tagNames ?? []),
|
|
196
|
+
mode: input.mode === "or" ? "or" : "and",
|
|
197
|
+
propertyFilters: normalizeViewPropertyFilters(input.propertyFilters),
|
|
198
|
+
sortBy: normalizeViewSortBy(input.sortBy),
|
|
199
|
+
sortOrder: normalizeViewSortOrder(input.sortOrder)
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
const normalizeViewNotesPagination = (pagination) => {
|
|
203
|
+
const numericLimit = Number(pagination?.limit ?? DEFAULT_VIEW_NOTES_QUERY_LIMIT);
|
|
204
|
+
const limit = Number.isInteger(numericLimit) ? Math.min(MAX_VIEW_NOTES_QUERY_LIMIT, Math.max(1, numericLimit)) : DEFAULT_VIEW_NOTES_QUERY_LIMIT;
|
|
205
|
+
const numericOffset = Number(pagination?.offset ?? 0);
|
|
206
|
+
const offset = Number.isInteger(numericOffset) ? Math.max(0, numericOffset) : 0;
|
|
207
|
+
return { limit, offset };
|
|
208
|
+
};
|
|
65
209
|
const pickNextActiveViewTabId = (tabIds, deletedTabId, currentActiveTabId) => {
|
|
66
210
|
const remainingTabIds = tabIds.filter((tabId) => tabId !== deletedTabId);
|
|
67
211
|
if (remainingTabIds.length === 0) {
|
|
@@ -94,15 +238,22 @@ const parseViewId = (id) => {
|
|
|
94
238
|
}
|
|
95
239
|
return numericId;
|
|
96
240
|
};
|
|
97
|
-
const serializeViewSection = (section) =>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
241
|
+
const serializeViewSection = (section) => {
|
|
242
|
+
const query = parseStoredViewQuery(section.query);
|
|
243
|
+
return {
|
|
244
|
+
id: String(section.id),
|
|
245
|
+
tabId: String(section.tabId),
|
|
246
|
+
title: section.title,
|
|
247
|
+
displayType: normalizeViewDisplayType(section.displayType),
|
|
248
|
+
tagNames: section.tags.map((tag) => tag.name),
|
|
249
|
+
mode: section.mode,
|
|
250
|
+
propertyFilters: query.propertyFilters,
|
|
251
|
+
sortBy: query.sortBy,
|
|
252
|
+
sortOrder: query.sortOrder,
|
|
253
|
+
limit: section.limit,
|
|
254
|
+
order: section.order
|
|
255
|
+
};
|
|
256
|
+
};
|
|
106
257
|
const serializeViewTab = (tab) => ({
|
|
107
258
|
id: String(tab.id),
|
|
108
259
|
title: tab.title,
|
|
@@ -137,6 +288,52 @@ const readViewSection = async (db, id) => {
|
|
|
137
288
|
});
|
|
138
289
|
return section ? serializeViewSection(section) : null;
|
|
139
290
|
};
|
|
291
|
+
const hydratePropertyFilters = async (db, filters, options = {}) => {
|
|
292
|
+
if (filters.length === 0) {
|
|
293
|
+
return [];
|
|
294
|
+
}
|
|
295
|
+
const definitions = await db.propertyDefinition.findMany({
|
|
296
|
+
where: { key: { in: filters.map((filter) => filter.key) } },
|
|
297
|
+
select: {
|
|
298
|
+
key: true,
|
|
299
|
+
name: true,
|
|
300
|
+
valueType: true,
|
|
301
|
+
options: {
|
|
302
|
+
select: {
|
|
303
|
+
value: true
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
const definitionByKey = new Map(definitions.map((definition) => [definition.key, definition]));
|
|
309
|
+
return filters.map((filter) => {
|
|
310
|
+
const definition = definitionByKey.get(filter.key);
|
|
311
|
+
if (!definition) {
|
|
312
|
+
throw new InvalidNotePropertyInputError(`Property ${filter.key} is not defined.`);
|
|
313
|
+
}
|
|
314
|
+
if (definition.valueType !== filter.valueType) {
|
|
315
|
+
throw new InvalidNotePropertyInputError(`Property ${filter.key} uses ${definition.valueType} values.`);
|
|
316
|
+
}
|
|
317
|
+
if (options.validateSelectOptions && filter.valueType === "select" && filter.operator !== "exists" && filter.operator !== "notExists") {
|
|
318
|
+
const optionValue = normalizeSelectFilterValue(filter.value ?? "");
|
|
319
|
+
if (!definition.options.some((option) => option.value === optionValue)) {
|
|
320
|
+
throw new InvalidNotePropertyInputError(`Property ${filter.key} option ${optionValue} is not defined.`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
...filter,
|
|
325
|
+
name: definition.name
|
|
326
|
+
};
|
|
327
|
+
});
|
|
328
|
+
};
|
|
329
|
+
const buildStoredViewQuery = async (db, section) => {
|
|
330
|
+
const propertyFilters = await hydratePropertyFilters(db, section.propertyFilters, { validateSelectOptions: true });
|
|
331
|
+
return serializeStoredViewQuery({
|
|
332
|
+
propertyFilters,
|
|
333
|
+
sortBy: section.sortBy,
|
|
334
|
+
sortOrder: section.sortOrder
|
|
335
|
+
});
|
|
336
|
+
};
|
|
140
337
|
const getNextTabOrder = async (db) => {
|
|
141
338
|
const lastTab = await db.viewTab.findFirst({
|
|
142
339
|
orderBy: [{ order: "desc" }, { createdAt: "desc" }],
|
|
@@ -187,6 +384,94 @@ const getViewWorkspace = async () => {
|
|
|
187
384
|
const getViewSectionById = async (id) => {
|
|
188
385
|
return readViewSection(models, parseViewId(id));
|
|
189
386
|
};
|
|
387
|
+
const normalizeSelectFilterValue = (value) => {
|
|
388
|
+
return value.trim().toLowerCase().replace(/\s+/g, "-");
|
|
389
|
+
};
|
|
390
|
+
const buildPropertyFilterValueWhere = (filter) => {
|
|
391
|
+
switch (filter.valueType) {
|
|
392
|
+
case "number": {
|
|
393
|
+
const numberValue = Number(filter.value);
|
|
394
|
+
if (filter.operator === "before") {
|
|
395
|
+
return { numberValue: { lt: numberValue } };
|
|
396
|
+
}
|
|
397
|
+
if (filter.operator === "after") {
|
|
398
|
+
return { numberValue: { gt: numberValue } };
|
|
399
|
+
}
|
|
400
|
+
return { numberValue };
|
|
401
|
+
}
|
|
402
|
+
case "date": {
|
|
403
|
+
const dateValue = /* @__PURE__ */ new Date(`${filter.value}T00:00:00.000Z`);
|
|
404
|
+
if (filter.operator === "before") {
|
|
405
|
+
return { dateValue: { lt: dateValue } };
|
|
406
|
+
}
|
|
407
|
+
if (filter.operator === "after") {
|
|
408
|
+
return { dateValue: { gt: dateValue } };
|
|
409
|
+
}
|
|
410
|
+
return { dateValue };
|
|
411
|
+
}
|
|
412
|
+
case "boolean":
|
|
413
|
+
return { boolValue: filter.value === "true" };
|
|
414
|
+
case "select":
|
|
415
|
+
return { option: { is: { value: normalizeSelectFilterValue(filter.value ?? "") } } };
|
|
416
|
+
case "url":
|
|
417
|
+
case "text":
|
|
418
|
+
default:
|
|
419
|
+
return { textValueNormalized: filter.value?.toLowerCase() ?? "" };
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
const buildPropertyFilterWhere = (filter) => {
|
|
423
|
+
const definitionWhere = {
|
|
424
|
+
definition: {
|
|
425
|
+
is: {
|
|
426
|
+
key: filter.key
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
if (filter.operator === "notExists") {
|
|
431
|
+
return {
|
|
432
|
+
properties: {
|
|
433
|
+
none: definitionWhere
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
if (filter.operator === "exists") {
|
|
438
|
+
return {
|
|
439
|
+
properties: {
|
|
440
|
+
some: definitionWhere
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
properties: {
|
|
446
|
+
some: {
|
|
447
|
+
...definitionWhere,
|
|
448
|
+
...buildPropertyFilterValueWhere(filter)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
};
|
|
453
|
+
const buildViewNotesWhere = (query) => {
|
|
454
|
+
const clauses = [];
|
|
455
|
+
if (query.tagNames.length > 0) {
|
|
456
|
+
clauses.push(buildNoteTagNamesWhere(query.tagNames, query.mode));
|
|
457
|
+
}
|
|
458
|
+
for (const filter of query.propertyFilters) {
|
|
459
|
+
clauses.push(buildPropertyFilterWhere(filter));
|
|
460
|
+
}
|
|
461
|
+
if (clauses.length === 0) {
|
|
462
|
+
return {};
|
|
463
|
+
}
|
|
464
|
+
return { AND: clauses };
|
|
465
|
+
};
|
|
466
|
+
const buildViewSectionWhere = (section) => {
|
|
467
|
+
return buildViewNotesWhere(section);
|
|
468
|
+
};
|
|
469
|
+
const buildViewNotesOrderBy = (query) => {
|
|
470
|
+
return [{ [query.sortBy]: query.sortOrder }, { id: "asc" }];
|
|
471
|
+
};
|
|
472
|
+
const buildViewSectionOrderBy = (section) => {
|
|
473
|
+
return buildViewNotesOrderBy(section);
|
|
474
|
+
};
|
|
190
475
|
const getViewSectionNotes = async (id, pagination) => {
|
|
191
476
|
const section = await models.viewSection.findUnique({
|
|
192
477
|
where: { id: parseViewId(id) },
|
|
@@ -195,18 +480,12 @@ const getViewSectionNotes = async (id, pagination) => {
|
|
|
195
480
|
if (!section) {
|
|
196
481
|
return null;
|
|
197
482
|
}
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
totalCount: 0,
|
|
202
|
-
notes: []
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
const where = buildNoteTagNamesWhere(tagNames, section.mode);
|
|
483
|
+
const serializedSection = serializeViewSection(section);
|
|
484
|
+
const where = buildViewSectionWhere(serializedSection);
|
|
206
485
|
const [totalCount, notes] = await Promise.all([
|
|
207
486
|
models.note.count({ where }),
|
|
208
487
|
models.note.findMany({
|
|
209
|
-
orderBy:
|
|
488
|
+
orderBy: buildViewSectionOrderBy(serializedSection),
|
|
210
489
|
where,
|
|
211
490
|
take: Number(pagination.limit),
|
|
212
491
|
skip: Number(pagination.offset)
|
|
@@ -217,6 +496,33 @@ const getViewSectionNotes = async (id, pagination) => {
|
|
|
217
496
|
notes
|
|
218
497
|
};
|
|
219
498
|
};
|
|
499
|
+
const getNotesByProperties = async (input, pagination) => {
|
|
500
|
+
const normalizedQuery = normalizeViewNotesQueryInput(input);
|
|
501
|
+
if (normalizedQuery.propertyFilters.length === 0) {
|
|
502
|
+
throw new InvalidNotePropertyInputError("At least one property filter is required.");
|
|
503
|
+
}
|
|
504
|
+
const query = {
|
|
505
|
+
...normalizedQuery,
|
|
506
|
+
propertyFilters: await hydratePropertyFilters(models, normalizedQuery.propertyFilters, {
|
|
507
|
+
validateSelectOptions: true
|
|
508
|
+
})
|
|
509
|
+
};
|
|
510
|
+
const normalizedPagination = normalizeViewNotesPagination(pagination);
|
|
511
|
+
const where = buildViewNotesWhere(query);
|
|
512
|
+
const [totalCount, notes] = await Promise.all([
|
|
513
|
+
models.note.count({ where }),
|
|
514
|
+
models.note.findMany({
|
|
515
|
+
orderBy: buildViewNotesOrderBy(query),
|
|
516
|
+
where,
|
|
517
|
+
take: normalizedPagination.limit,
|
|
518
|
+
skip: normalizedPagination.offset
|
|
519
|
+
})
|
|
520
|
+
]);
|
|
521
|
+
return {
|
|
522
|
+
totalCount,
|
|
523
|
+
notes
|
|
524
|
+
};
|
|
525
|
+
};
|
|
220
526
|
const createViewTab = async (title) => {
|
|
221
527
|
return models.$transaction(async (tx) => {
|
|
222
528
|
const workspace = await ensureViewWorkspace(tx);
|
|
@@ -323,10 +629,13 @@ const createViewSection = async (tabId, input) => {
|
|
|
323
629
|
throw new Error("View tab not found.");
|
|
324
630
|
}
|
|
325
631
|
const nextSectionOrder = await getNextSectionOrder(tx, numericTabId);
|
|
632
|
+
const query = await buildStoredViewQuery(tx, nextSection);
|
|
326
633
|
const createdSection = await tx.viewSection.create({
|
|
327
634
|
data: {
|
|
328
635
|
tabId: numericTabId,
|
|
329
636
|
title: nextSection.title,
|
|
637
|
+
displayType: nextSection.displayType,
|
|
638
|
+
query,
|
|
330
639
|
mode: nextSection.mode,
|
|
331
640
|
limit: nextSection.limit,
|
|
332
641
|
order: nextSectionOrder,
|
|
@@ -345,23 +654,28 @@ const createViewSection = async (tabId, input) => {
|
|
|
345
654
|
const updateViewSection = async (id, input) => {
|
|
346
655
|
const sectionId = parseViewId(id);
|
|
347
656
|
const nextSection = normalizeViewSectionInput(input);
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
657
|
+
return models.$transaction(async (tx) => {
|
|
658
|
+
const query = await buildStoredViewQuery(tx, nextSection);
|
|
659
|
+
const updatedSection = await tx.viewSection.update({
|
|
660
|
+
where: { id: sectionId },
|
|
661
|
+
data: {
|
|
662
|
+
title: nextSection.title,
|
|
663
|
+
displayType: nextSection.displayType,
|
|
664
|
+
query,
|
|
665
|
+
mode: nextSection.mode,
|
|
666
|
+
limit: nextSection.limit,
|
|
667
|
+
tags: {
|
|
668
|
+
deleteMany: {},
|
|
669
|
+
create: nextSection.tagNames.map((tagName, index) => ({
|
|
670
|
+
name: tagName,
|
|
671
|
+
order: index
|
|
672
|
+
}))
|
|
673
|
+
}
|
|
674
|
+
},
|
|
675
|
+
include: orderedViewSectionTagsInclude
|
|
676
|
+
});
|
|
677
|
+
return serializeViewSection(updatedSection);
|
|
363
678
|
});
|
|
364
|
-
return serializeViewSection(updatedSection);
|
|
365
679
|
};
|
|
366
680
|
const deleteViewSection = async (id) => {
|
|
367
681
|
const sectionId = parseViewId(id);
|
|
@@ -408,17 +722,27 @@ const reorderViewSections = async (tabId, sectionIds) => {
|
|
|
408
722
|
});
|
|
409
723
|
};
|
|
410
724
|
export {
|
|
725
|
+
DEFAULT_VIEW_NOTES_QUERY_LIMIT,
|
|
411
726
|
DEFAULT_VIEW_SECTION_LIMIT,
|
|
727
|
+
MAX_VIEW_NOTES_QUERY_LIMIT,
|
|
412
728
|
MAX_VIEW_SECTION_LIMIT,
|
|
413
729
|
MIN_VIEW_SECTION_LIMIT,
|
|
730
|
+
buildPropertyFilterWhere,
|
|
731
|
+
buildViewNotesWhere,
|
|
732
|
+
buildViewSectionWhere,
|
|
414
733
|
clampViewSectionLimit,
|
|
415
734
|
createViewSection,
|
|
416
735
|
createViewTab,
|
|
417
736
|
deleteViewSection,
|
|
418
737
|
deleteViewTab,
|
|
738
|
+
getNotesByProperties,
|
|
419
739
|
getViewSectionById,
|
|
420
740
|
getViewSectionNotes,
|
|
421
741
|
getViewWorkspace,
|
|
742
|
+
hydratePropertyFilters,
|
|
743
|
+
normalizeViewNotesPagination,
|
|
744
|
+
normalizeViewNotesQueryInput,
|
|
745
|
+
normalizeViewPropertyFilters,
|
|
422
746
|
normalizeViewSectionInput,
|
|
423
747
|
normalizeViewTabTitle,
|
|
424
748
|
normalizeViewTagNames,
|