@fabriktor/fx 0.0.37 → 0.0.39

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.
@@ -1,5 +1,8 @@
1
- import { type Chat, type ChatRoom, type ChatRoomMember } from "@fabriktor/schema";
2
1
  import type { ChatOperator, WSCloseHandler, WSConnection, WSErrorHandler, WSMessageHandler } from "@fabriktor/client";
2
+ import { type Chat, type ChatRoom, type ChatRoomMember } from "@fabriktor/schema";
3
+ import { type SearchPage } from "../core/search.js";
4
+ type ChatInsertOptions = Parameters<ChatOperator["insertOneChat"]>[0];
5
+ export type CreateChatOptions = ChatInsertOptions;
3
6
  export type ReactChatOptions = Parameters<ChatOperator["reactChat"]>[0];
4
7
  export type ChatFXOptions = {
5
8
  chat: ChatOperator;
@@ -27,6 +30,13 @@ export type LatestChatsPage = {
27
30
  has_more: boolean;
28
31
  next_cursor: LatestChatsCursor;
29
32
  };
33
+ export type SearchChatRoomMembersOptions = {
34
+ chat_room_id: string;
35
+ q?: string;
36
+ page?: number;
37
+ per_page?: number;
38
+ };
39
+ export type SearchChatRoomMembersPage = SearchPage<ChatRoomMember>;
30
40
  export type ChatRoomMembersOptions = {
31
41
  chat_room_id: string;
32
42
  };
@@ -53,22 +63,24 @@ export type ReactUploadedChatOptions = {
53
63
  reaction: ReactChatOptions;
54
64
  };
55
65
  export type ChatRoomSession = {
56
- latest: LatestChatsPage;
57
66
  chats_ws: WSConnection;
58
67
  chat_room_ws: WSConnection;
68
+ latest: LatestChatsPage;
59
69
  closeNormal(reason?: string): void;
60
70
  closeLeavingPage(reason?: string): void;
61
71
  };
62
72
  export type GlobalChatRoomsSession = {
63
- members: ChatRoomMember[];
64
73
  chat_rooms_ws: WSConnection;
74
+ members: ChatRoomMember[];
65
75
  closeNormal(reason?: string): void;
66
76
  closeLeavingPage(reason?: string): void;
67
77
  };
68
78
  export interface ChatFXOperator {
79
+ createChat(opts: CreateChatOptions): Promise<Chat>;
69
80
  newLatestChatsCursor(opts: LatestChatsOptions): LatestChatsCursor;
70
81
  latestChats(opts: LatestChatsOptions): Promise<LatestChatsPage>;
71
82
  nextLatestChats(opts: NextLatestChatsOptions): Promise<LatestChatsPage>;
83
+ searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
72
84
  chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
73
85
  myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
74
86
  openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
@@ -78,13 +90,19 @@ export interface ChatFXOperator {
78
90
  export declare class ChatFX implements ChatFXOperator {
79
91
  private chat;
80
92
  constructor(opts: ChatFXOptions);
93
+ createChat(opts: CreateChatOptions): Promise<Chat>;
81
94
  newLatestChatsCursor(opts: LatestChatsOptions): LatestChatsCursor;
82
95
  latestChats(opts: LatestChatsOptions): Promise<LatestChatsPage>;
83
96
  nextLatestChats(opts: NextLatestChatsOptions): Promise<LatestChatsPage>;
97
+ searchChatRoomMembers(opts: SearchChatRoomMembersOptions): Promise<SearchChatRoomMembersPage>;
84
98
  chatRoomMembers(opts: ChatRoomMembersOptions): Promise<ChatRoomMember[]>;
85
99
  myChatRoomMembers(opts: MyChatRoomMembersOptions): Promise<ChatRoomMember[]>;
86
100
  openChatRoom(opts: OpenChatRoomOptions): Promise<ChatRoomSession>;
87
101
  openGlobalChatRooms(opts: OpenGlobalChatRoomsOptions): Promise<GlobalChatRoomsSession>;
88
102
  reactUploadedChat(opts: ReactUploadedChatOptions): Promise<void>;
103
+ private openChatRoomConnections;
104
+ private openGlobalChatRoomsConnection;
105
+ private findChat;
89
106
  }
90
107
  export declare function newChatFX(opts: ChatFXOptions): ChatFX;
108
+ export {};
@@ -3,40 +3,50 @@
3
3
  // Licensed under the Apache License, Version 2.0 (the "License"); you may
4
4
  // not use this file except in compliance with the License. You may obtain
5
5
  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- import { SearchPageKey, SearchPerPageKey, SearchResultResultsKey, } from "@fabriktor/schema";
7
- import { errValidation } from "../core/err.js";
8
- const firstPage = 1;
9
- const defaultPerPage = 30;
6
+ import { ZeroID, } from "@fabriktor/schema";
7
+ import { errResolveFailed, errValidation } from "../core/err.js";
8
+ import { normalizeSearchPage, normalizeSearchPerPage, runSearch, } from "../core/search.js";
10
9
  const closeCodeNormal = 1000;
11
10
  const closeCodeLeavingPage = 1001;
11
+ const wsRefreshMilliseconds = 40 * 60 * 1000;
12
+ const wsRefreshRetryMilliseconds = 30 * 1000;
13
+ const resourceChatID = "chat_id";
14
+ const resourceChat = "chat";
15
+ const msgCreatedChatIDMissing = "Created chat response did not contain an ID.";
16
+ const msgChatMissing = "Chat could not be resolved after the mutation.";
12
17
  const msgChatObjectsUploading = "Cannot react to a chat while objects are still uploading.";
13
18
  export class ChatFX {
14
19
  chat;
15
20
  constructor(opts) {
16
21
  this.chat = opts.chat;
17
22
  }
23
+ async createChat(opts) {
24
+ const out = await this.chat.insertOneChat(opts);
25
+ const id = requiredOperationID(out, {
26
+ resource: resourceChatID,
27
+ msg: msgCreatedChatIDMissing,
28
+ });
29
+ return await this.findChat(id);
30
+ }
18
31
  newLatestChatsCursor(opts) {
19
32
  return {
20
33
  chat_room_id: opts.chat_room_id,
21
- next_page: normalizePage(opts.page),
22
- per_page: normalizePerPage(opts.per_page),
34
+ next_page: normalizeSearchPage(opts.page),
35
+ per_page: normalizeSearchPerPage(opts.per_page),
23
36
  done: false,
24
37
  };
25
38
  }
26
39
  async latestChats(opts) {
27
- const page = normalizePage(opts.page);
28
- const per_page = normalizePerPage(opts.per_page);
29
- const raw = await this.chat.searchManyChats({
40
+ const raw = await runSearch({
41
+ search: (searchOpts) => this.chat.searchManyChats(searchOpts),
30
42
  query: {
31
43
  chat_room_id: opts.chat_room_id,
32
- [SearchPageKey]: page,
33
- [SearchPerPageKey]: per_page,
34
44
  },
45
+ page: opts.page,
46
+ per_page: opts.per_page,
35
47
  });
36
48
  return latestChatsPage({
37
49
  chat_room_id: opts.chat_room_id,
38
- page,
39
- per_page,
40
50
  raw,
41
51
  });
42
52
  }
@@ -50,6 +60,17 @@ export class ChatFX {
50
60
  per_page: opts.cursor.per_page,
51
61
  });
52
62
  }
63
+ async searchChatRoomMembers(opts) {
64
+ return await runSearch({
65
+ search: (searchOpts) => this.chat.searchManyChatRoomMembers(searchOpts),
66
+ query: {
67
+ chat_room_id: opts.chat_room_id,
68
+ },
69
+ q: opts.q,
70
+ page: opts.page,
71
+ per_page: opts.per_page,
72
+ });
73
+ }
53
74
  async chatRoomMembers(opts) {
54
75
  const out = await this.chat.queryChatRoomMembers({
55
76
  query: {
@@ -71,28 +92,66 @@ export class ChatFX {
71
92
  chat_room_id: opts.chat_room_id,
72
93
  per_page: opts.per_page,
73
94
  });
74
- const chats_ws = await this.chat.wsChats({
75
- owner_id: opts.owner_id,
76
- chat_room_id: opts.chat_room_id,
77
- on_message: opts.on_chat,
78
- on_error: opts.on_error,
79
- on_close: opts.on_close,
80
- });
81
- const chat_room_ws = await this.chat.wsChatRoom({
82
- id: opts.chat_room_id,
83
- on_message: opts.on_chat_room,
84
- on_error: opts.on_error,
85
- on_close: opts.on_close,
86
- });
95
+ const fx = this;
96
+ let current = await fx.openChatRoomConnections(opts);
97
+ let refresh_timer = null;
98
+ let closed = false;
99
+ current.activate();
100
+ scheduleRefresh();
101
+ async function refresh() {
102
+ refresh_timer = null;
103
+ if (closed) {
104
+ return;
105
+ }
106
+ try {
107
+ const next = await fx.openChatRoomConnections(opts);
108
+ if (closed) {
109
+ next.close(closeCodeNormal);
110
+ return;
111
+ }
112
+ const previous = current;
113
+ previous.deactivate();
114
+ current = next;
115
+ current.activate();
116
+ previous.close(closeCodeNormal);
117
+ scheduleRefresh();
118
+ }
119
+ catch {
120
+ scheduleRefresh(wsRefreshRetryMilliseconds);
121
+ }
122
+ }
123
+ function scheduleRefresh(delay = wsRefreshMilliseconds) {
124
+ if (closed) {
125
+ return;
126
+ }
127
+ refresh_timer = setTimeout(() => {
128
+ void refresh();
129
+ }, delay);
130
+ }
131
+ function closeSession(code, reason) {
132
+ if (closed) {
133
+ return;
134
+ }
135
+ closed = true;
136
+ if (refresh_timer !== null) {
137
+ clearTimeout(refresh_timer);
138
+ refresh_timer = null;
139
+ }
140
+ current.close(code, reason);
141
+ }
87
142
  return {
88
143
  latest,
89
- chats_ws,
90
- chat_room_ws,
144
+ get chats_ws() {
145
+ return current.chats_ws;
146
+ },
147
+ get chat_room_ws() {
148
+ return current.chat_room_ws;
149
+ },
91
150
  closeNormal(reason) {
92
- closeWS([chats_ws, chat_room_ws], closeCodeNormal, reason);
151
+ closeSession(closeCodeNormal, reason);
93
152
  },
94
153
  closeLeavingPage(reason) {
95
- closeWS([chats_ws, chat_room_ws], closeCodeLeavingPage, reason);
154
+ closeSession(closeCodeLeavingPage, reason);
96
155
  },
97
156
  };
98
157
  }
@@ -100,20 +159,63 @@ export class ChatFX {
100
159
  const members = await this.myChatRoomMembers({
101
160
  owner_id: opts.owner_id,
102
161
  });
103
- const chat_rooms_ws = await this.chat.wsMineChatRooms({
104
- owner_id: opts.owner_id,
105
- on_message: opts.on_chat_room,
106
- on_error: opts.on_error,
107
- on_close: opts.on_close,
108
- });
162
+ const fx = this;
163
+ let current = await fx.openGlobalChatRoomsConnection(opts);
164
+ let refresh_timer = null;
165
+ let closed = false;
166
+ current.activate();
167
+ scheduleRefresh();
168
+ async function refresh() {
169
+ refresh_timer = null;
170
+ if (closed) {
171
+ return;
172
+ }
173
+ try {
174
+ const next = await fx.openGlobalChatRoomsConnection(opts);
175
+ if (closed) {
176
+ next.close(closeCodeNormal);
177
+ return;
178
+ }
179
+ const previous = current;
180
+ previous.deactivate();
181
+ current = next;
182
+ current.activate();
183
+ previous.close(closeCodeNormal);
184
+ scheduleRefresh();
185
+ }
186
+ catch {
187
+ scheduleRefresh(wsRefreshRetryMilliseconds);
188
+ }
189
+ }
190
+ function scheduleRefresh(delay = wsRefreshMilliseconds) {
191
+ if (closed) {
192
+ return;
193
+ }
194
+ refresh_timer = setTimeout(() => {
195
+ void refresh();
196
+ }, delay);
197
+ }
198
+ function closeSession(code, reason) {
199
+ if (closed) {
200
+ return;
201
+ }
202
+ closed = true;
203
+ if (refresh_timer !== null) {
204
+ clearTimeout(refresh_timer);
205
+ refresh_timer = null;
206
+ }
207
+ current.close(code, reason);
208
+ }
109
209
  return {
110
210
  members,
111
- chat_rooms_ws,
211
+ get chat_rooms_ws() {
212
+ return current.chat_rooms_ws;
213
+ },
112
214
  closeNormal(reason) {
113
- closeWS([chat_rooms_ws], closeCodeNormal, reason);
215
+ closeSession(closeCodeNormal, reason);
114
216
  },
115
217
  closeLeavingPage(reason) {
116
- closeWS([chat_rooms_ws], closeCodeLeavingPage, reason);
218
+ closeSession(closeCodeLeavingPage, reason);
117
219
  },
118
220
  };
119
221
  }
@@ -126,34 +228,156 @@ export class ChatFX {
126
228
  }
127
229
  await this.chat.reactChat(opts.reaction);
128
230
  }
231
+ async openChatRoomConnections(opts) {
232
+ let active = false;
233
+ const chats_ws = await this.chat.wsChats({
234
+ owner_id: opts.owner_id,
235
+ chat_room_id: opts.chat_room_id,
236
+ on_message(v, ev) {
237
+ if (active) {
238
+ opts.on_chat(v, ev);
239
+ }
240
+ },
241
+ on_error: opts.on_error === undefined
242
+ ? undefined
243
+ : (ev) => {
244
+ if (active) {
245
+ opts.on_error?.(ev);
246
+ }
247
+ },
248
+ on_close: opts.on_close === undefined
249
+ ? undefined
250
+ : (ev) => {
251
+ if (active) {
252
+ opts.on_close?.(ev);
253
+ }
254
+ },
255
+ });
256
+ let chat_room_ws;
257
+ try {
258
+ chat_room_ws = await this.chat.wsChatRoom({
259
+ id: opts.chat_room_id,
260
+ on_message(v, ev) {
261
+ if (active) {
262
+ opts.on_chat_room(v, ev);
263
+ }
264
+ },
265
+ on_error: opts.on_error === undefined
266
+ ? undefined
267
+ : (ev) => {
268
+ if (active) {
269
+ opts.on_error?.(ev);
270
+ }
271
+ },
272
+ on_close: opts.on_close === undefined
273
+ ? undefined
274
+ : (ev) => {
275
+ if (active) {
276
+ opts.on_close?.(ev);
277
+ }
278
+ },
279
+ });
280
+ }
281
+ catch (cause) {
282
+ chats_ws.close(closeCodeNormal);
283
+ await Promise.allSettled([chats_ws.opened]);
284
+ throw cause;
285
+ }
286
+ try {
287
+ await Promise.all([chats_ws.opened, chat_room_ws.opened]);
288
+ }
289
+ catch (cause) {
290
+ closeWS([chats_ws, chat_room_ws], closeCodeNormal);
291
+ throw cause;
292
+ }
293
+ return {
294
+ chats_ws,
295
+ chat_room_ws,
296
+ activate() {
297
+ active = true;
298
+ },
299
+ deactivate() {
300
+ active = false;
301
+ },
302
+ close(code, reason) {
303
+ closeWS([chats_ws, chat_room_ws], code, reason);
304
+ },
305
+ };
306
+ }
307
+ async openGlobalChatRoomsConnection(opts) {
308
+ let active = false;
309
+ const chat_rooms_ws = await this.chat.wsMineChatRooms({
310
+ owner_id: opts.owner_id,
311
+ on_message(v, ev) {
312
+ if (active) {
313
+ opts.on_chat_room(v, ev);
314
+ }
315
+ },
316
+ on_error: opts.on_error === undefined
317
+ ? undefined
318
+ : (ev) => {
319
+ if (active) {
320
+ opts.on_error?.(ev);
321
+ }
322
+ },
323
+ on_close: opts.on_close === undefined
324
+ ? undefined
325
+ : (ev) => {
326
+ if (active) {
327
+ opts.on_close?.(ev);
328
+ }
329
+ },
330
+ });
331
+ try {
332
+ await chat_rooms_ws.opened;
333
+ }
334
+ catch (cause) {
335
+ chat_rooms_ws.close(closeCodeNormal);
336
+ throw cause;
337
+ }
338
+ return {
339
+ chat_rooms_ws,
340
+ activate() {
341
+ active = true;
342
+ },
343
+ deactivate() {
344
+ active = false;
345
+ },
346
+ close(code, reason) {
347
+ chat_rooms_ws.close(code, reason);
348
+ },
349
+ };
350
+ }
351
+ async findChat(id) {
352
+ const out = await this.chat.findOneChat({
353
+ id,
354
+ });
355
+ const chat = out.value;
356
+ if (chat === undefined) {
357
+ throw errResolveFailed({
358
+ resource: resourceChat,
359
+ msg: msgChatMissing,
360
+ });
361
+ }
362
+ return chat;
363
+ }
129
364
  }
130
365
  export function newChatFX(opts) {
131
366
  return new ChatFX(opts);
132
367
  }
133
368
  function latestChatsPage(opts) {
134
- const value = opts.raw.value;
135
- const chats = value?.[SearchResultResultsKey] ?? [];
136
- const total_count = value?.total_count;
137
- const last_page = value?.last_page;
138
- const has_more = hasMoreChats({
139
- chats,
140
- page: opts.page,
141
- per_page: opts.per_page,
142
- total_count,
143
- last_page,
144
- });
145
369
  return {
146
- chats,
147
- page: opts.page,
148
- per_page: opts.per_page,
149
- total_count,
150
- last_page,
151
- has_more,
370
+ chats: opts.raw.results,
371
+ page: opts.raw.page,
372
+ per_page: opts.raw.per_page,
373
+ total_count: opts.raw.total_count,
374
+ last_page: opts.raw.last_page,
375
+ has_more: opts.raw.has_more,
152
376
  next_cursor: {
153
377
  chat_room_id: opts.chat_room_id,
154
- next_page: has_more ? opts.page + 1 : opts.page,
155
- per_page: opts.per_page,
156
- done: !has_more,
378
+ next_page: opts.raw.next_page,
379
+ per_page: opts.raw.per_page,
380
+ done: opts.raw.done,
157
381
  },
158
382
  };
159
383
  }
@@ -166,29 +390,25 @@ function emptyLatestChatsPage(cursor) {
166
390
  next_cursor: cursor,
167
391
  };
168
392
  }
169
- function hasMoreChats(opts) {
170
- if (opts.last_page !== undefined) {
171
- return opts.page < opts.last_page;
172
- }
173
- if (opts.total_count !== undefined) {
174
- return opts.page * opts.per_page < opts.total_count;
175
- }
176
- return opts.chats.length >= opts.per_page;
177
- }
178
393
  function closeWS(conns, code, reason) {
179
394
  for (const conn of conns) {
180
395
  conn.close(code, reason);
181
396
  }
182
397
  }
183
- function normalizePage(v) {
184
- if (v === undefined || !Number.isFinite(v)) {
185
- return firstPage;
398
+ function requiredOperationID(out, opts) {
399
+ const id = resolvedID(out.id);
400
+ if (id === undefined) {
401
+ throw errResolveFailed({
402
+ resource: opts.resource,
403
+ msg: opts.msg,
404
+ });
186
405
  }
187
- return Math.max(firstPage, Math.trunc(v));
406
+ return id;
188
407
  }
189
- function normalizePerPage(v) {
190
- if (v === undefined || !Number.isFinite(v)) {
191
- return defaultPerPage;
408
+ function resolvedID(value) {
409
+ const id = value?.trim();
410
+ if (id === undefined || id === "" || id === ZeroID) {
411
+ return undefined;
192
412
  }
193
- return Math.max(1, Math.trunc(v));
413
+ return id;
194
414
  }
@@ -0,0 +1,24 @@
1
+ import type { OperationResultOf, SearchManyOptionsCRUD, SearchResultOf } from "@fabriktor/client";
2
+ export type SearchQuery = NonNullable<SearchManyOptionsCRUD["query"]>;
3
+ export type SearchCall<T> = (opts: SearchManyOptionsCRUD) => Promise<OperationResultOf<SearchResultOf<T>>>;
4
+ export type RunSearchOptions<T> = {
5
+ search: SearchCall<T>;
6
+ query?: SearchQuery;
7
+ q?: string;
8
+ page?: number;
9
+ per_page?: number;
10
+ };
11
+ export type SearchPage<T> = {
12
+ results: T[];
13
+ page: number;
14
+ per_page: number;
15
+ total_count?: number;
16
+ last_page?: number;
17
+ has_more: boolean;
18
+ next_page: number;
19
+ done: boolean;
20
+ };
21
+ export declare function runSearch<T>(opts: RunSearchOptions<T>): Promise<SearchPage<T>>;
22
+ export declare function normalizeSearchPage(value?: number): number;
23
+ export declare function normalizeSearchPerPage(value?: number): number;
24
+ export declare function normalizeSearchQuery(value?: string): string | undefined;
@@ -0,0 +1,84 @@
1
+ // Copyright (C) Fabriktor, Inc. 2025-present.
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+ // not use this file except in compliance with the License. You may obtain
5
+ // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
+ import { SearchPageKey, SearchPerPageKey, SearchQueryKey, SearchResultResultsKey, } from "@fabriktor/schema";
7
+ const firstSearchPage = 1;
8
+ const defaultSearchPerPage = 30;
9
+ export async function runSearch(opts) {
10
+ const page = normalizeSearchPage(opts.page);
11
+ const per_page = normalizeSearchPerPage(opts.per_page);
12
+ const q = normalizeSearchQuery(opts.q);
13
+ const query = q === undefined
14
+ ? {
15
+ ...opts.query,
16
+ [SearchPageKey]: page,
17
+ [SearchPerPageKey]: per_page,
18
+ }
19
+ : {
20
+ ...opts.query,
21
+ [SearchPageKey]: page,
22
+ [SearchPerPageKey]: per_page,
23
+ [SearchQueryKey]: q,
24
+ };
25
+ const raw = await opts.search({
26
+ query,
27
+ });
28
+ return resolveSearchPage({
29
+ raw,
30
+ page,
31
+ per_page,
32
+ });
33
+ }
34
+ export function normalizeSearchPage(value) {
35
+ if (value === undefined || !Number.isFinite(value)) {
36
+ return firstSearchPage;
37
+ }
38
+ return Math.max(firstSearchPage, Math.trunc(value));
39
+ }
40
+ export function normalizeSearchPerPage(value) {
41
+ if (value === undefined || !Number.isFinite(value)) {
42
+ return defaultSearchPerPage;
43
+ }
44
+ return Math.max(1, Math.trunc(value));
45
+ }
46
+ export function normalizeSearchQuery(value) {
47
+ const query = value?.trim();
48
+ if (!query) {
49
+ return undefined;
50
+ }
51
+ return query;
52
+ }
53
+ function resolveSearchPage(opts) {
54
+ const value = opts.raw.value;
55
+ const results = value?.[SearchResultResultsKey] ?? [];
56
+ const total_count = value?.total_count;
57
+ const last_page = value?.last_page;
58
+ const has_more = hasMoreSearchResults({
59
+ result_count: results.length,
60
+ page: opts.page,
61
+ per_page: opts.per_page,
62
+ total_count,
63
+ last_page,
64
+ });
65
+ return {
66
+ results,
67
+ page: opts.page,
68
+ per_page: opts.per_page,
69
+ total_count,
70
+ last_page,
71
+ has_more,
72
+ next_page: has_more ? opts.page + 1 : opts.page,
73
+ done: !has_more,
74
+ };
75
+ }
76
+ function hasMoreSearchResults(opts) {
77
+ if (opts.last_page !== undefined) {
78
+ return opts.page < opts.last_page;
79
+ }
80
+ if (opts.total_count !== undefined) {
81
+ return opts.page * opts.per_page < opts.total_count;
82
+ }
83
+ return opts.result_count >= opts.per_page;
84
+ }
@@ -5,6 +5,7 @@ export * from "./chat/chat.js";
5
5
  export * from "./contacts/contacts.js";
6
6
  export * from "./core/err.js";
7
7
  export * from "./core/pull.js";
8
+ export * from "./core/search.js";
8
9
  export * from "./feed/feed.js";
9
10
  export * from "./fx.js";
10
11
  export * from "./jobs/jobs.js";
package/dist/src/index.js CHANGED
@@ -5,6 +5,7 @@ export * from "./chat/chat.js";
5
5
  export * from "./contacts/contacts.js";
6
6
  export * from "./core/err.js";
7
7
  export * from "./core/pull.js";
8
+ export * from "./core/search.js";
8
9
  export * from "./feed/feed.js";
9
10
  export * from "./fx.js";
10
11
  export * from "./jobs/jobs.js";
@@ -1,5 +1,6 @@
1
1
  import type { MemosOperator } from "@fabriktor/client";
2
2
  import { type Memo, type MemoFolder } from "@fabriktor/schema";
3
+ import { type SearchPage } from "../core/search.js";
3
4
  type MemoInsertOptions = Parameters<MemosOperator["insertOneMemo"]>[0];
4
5
  type MemoReplaceOptions = Parameters<MemosOperator["replaceOneMemo"]>[0];
5
6
  type MemoFolderInsertOptions = Parameters<MemosOperator["insertOneMemoFolder"]>[0];
@@ -8,11 +9,27 @@ export type CreateMemoOptions = MemoInsertOptions;
8
9
  export type ReplaceMemoOptions = MemoReplaceOptions;
9
10
  export type CreateMemoFolderOptions = MemoFolderInsertOptions;
10
11
  export type ReplaceMemoFolderOptions = MemoFolderReplaceOptions;
12
+ export type SearchMemosOptions = {
13
+ owner_id: string;
14
+ q?: string;
15
+ page?: number;
16
+ per_page?: number;
17
+ };
18
+ export type SearchMemoFoldersOptions = {
19
+ owner_id: string;
20
+ q?: string;
21
+ page?: number;
22
+ per_page?: number;
23
+ };
24
+ export type SearchMemosPage = SearchPage<Memo>;
25
+ export type SearchMemoFoldersPage = SearchPage<MemoFolder>;
11
26
  export interface MemosFXOperator {
12
27
  createMemo(opts: CreateMemoOptions): Promise<Memo>;
13
28
  replaceMemo(opts: ReplaceMemoOptions): Promise<Memo>;
14
29
  createMemoFolder(opts: CreateMemoFolderOptions): Promise<MemoFolder>;
15
30
  replaceMemoFolder(opts: ReplaceMemoFolderOptions): Promise<MemoFolder>;
31
+ searchMemos(opts: SearchMemosOptions): Promise<SearchMemosPage>;
32
+ searchMemoFolders(opts: SearchMemoFoldersOptions): Promise<SearchMemoFoldersPage>;
16
33
  }
17
34
  export type MemosFXOptions = {
18
35
  memos: MemosOperator;
@@ -24,6 +41,8 @@ export declare class MemosFX implements MemosFXOperator {
24
41
  replaceMemo(opts: ReplaceMemoOptions): Promise<Memo>;
25
42
  createMemoFolder(opts: CreateMemoFolderOptions): Promise<MemoFolder>;
26
43
  replaceMemoFolder(opts: ReplaceMemoFolderOptions): Promise<MemoFolder>;
44
+ searchMemos(opts: SearchMemosOptions): Promise<SearchMemosPage>;
45
+ searchMemoFolders(opts: SearchMemoFoldersOptions): Promise<SearchMemoFoldersPage>;
27
46
  private findMemo;
28
47
  private findMemoFolder;
29
48
  }
@@ -3,8 +3,9 @@
3
3
  // Licensed under the Apache License, Version 2.0 (the "License"); you may
4
4
  // not use this file except in compliance with the License. You may obtain
5
5
  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- import { ZeroID, } from "@fabriktor/schema";
6
+ import { OwnerIDKey, ZeroID, } from "@fabriktor/schema";
7
7
  import { errResolveFailed } from "../core/err.js";
8
+ import { runSearch } from "../core/search.js";
8
9
  const resourceMemoID = "memo_id";
9
10
  const resourceMemo = "memo";
10
11
  const resourceMemoFolderID = "memo_folder_id";
@@ -42,6 +43,28 @@ export class MemosFX {
42
43
  await this.memos.replaceOneMemoFolder(opts);
43
44
  return await this.findMemoFolder(opts.id);
44
45
  }
46
+ async searchMemos(opts) {
47
+ return await runSearch({
48
+ search: (searchOpts) => this.memos.searchManyMemos(searchOpts),
49
+ query: {
50
+ [OwnerIDKey]: opts.owner_id,
51
+ },
52
+ q: opts.q,
53
+ page: opts.page,
54
+ per_page: opts.per_page,
55
+ });
56
+ }
57
+ async searchMemoFolders(opts) {
58
+ return await runSearch({
59
+ search: (searchOpts) => this.memos.searchManyMemoFolders(searchOpts),
60
+ query: {
61
+ [OwnerIDKey]: opts.owner_id,
62
+ },
63
+ q: opts.q,
64
+ page: opts.page,
65
+ per_page: opts.per_page,
66
+ });
67
+ }
45
68
  async findMemo(id) {
46
69
  const out = await this.memos.findOneMemo({
47
70
  id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabriktor/fx",
3
- "version": "0.0.37",
3
+ "version": "0.0.39",
4
4
  "description": "![Fabriktor Logo](./img/fabriktor-character.gif)",
5
5
  "type": "module",
6
6
  "exports": {
@@ -17,7 +17,7 @@
17
17
  "typescript": "^6.0.3"
18
18
  },
19
19
  "dependencies": {
20
- "@fabriktor/client": "0.0.43",
21
- "@fabriktor/schema": "0.0.31"
20
+ "@fabriktor/client": "0.0.45",
21
+ "@fabriktor/schema": "0.0.32"
22
22
  }
23
23
  }