Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F32162088
D15185.1765044518.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
D15185.1765044518.diff
View Options
diff --git a/lib/shared/threads/farcaster-group-spec.js b/lib/shared/threads/farcaster-group-spec.js
new file mode 100644
--- /dev/null
+++ b/lib/shared/threads/farcaster-group-spec.js
@@ -0,0 +1,15 @@
+// @flow
+
+import { farcasterThreadProtocol } from './protocols/farcaster-thread-protocol.js';
+import type { ThreadSpec } from './thread-spec.js';
+import type { MemberInfoSansPermissions } from '../../types/minimally-encoded-thread-permissions-types.js';
+
+const farcasterGroupSpec: ThreadSpec<MemberInfoSansPermissions> = Object.freeze(
+ {
+ traits: new Set(),
+ protocol: () => farcasterThreadProtocol,
+ threadLabel: 'Farcaster Group',
+ },
+);
+
+export { farcasterGroupSpec };
diff --git a/lib/shared/threads/farcaster-personal-spec.js b/lib/shared/threads/farcaster-personal-spec.js
new file mode 100644
--- /dev/null
+++ b/lib/shared/threads/farcaster-personal-spec.js
@@ -0,0 +1,14 @@
+// @flow
+
+import { farcasterThreadProtocol } from './protocols/farcaster-thread-protocol.js';
+import type { ThreadSpec } from './thread-spec.js';
+import type { MemberInfoSansPermissions } from '../../types/minimally-encoded-thread-permissions-types.js';
+
+const farcasterPersonalSpec: ThreadSpec<MemberInfoSansPermissions> =
+ Object.freeze({
+ traits: new Set(['personal']),
+ protocol: () => farcasterThreadProtocol,
+ threadLabel: 'Farcaster Personal',
+ });
+
+export { farcasterPersonalSpec };
diff --git a/lib/shared/threads/protocols/dm-thread-protocol.js b/lib/shared/threads/protocols/dm-thread-protocol.js
--- a/lib/shared/threads/protocols/dm-thread-protocol.js
+++ b/lib/shared/threads/protocols/dm-thread-protocol.js
@@ -51,7 +51,7 @@
import {
assertWithValidator,
pendingThickSidebarURLPrefix,
- thickIDRegex,
+ thickIDRegExp,
} from '../../../utils/validation-utils.js';
import { generatePendingThreadColor } from '../../color-utils.js';
import {
@@ -890,7 +890,7 @@
}: ThreadJoinPayload);
},
- threadIDMatchesProtocol: (threadID: string) => thickIDRegex.test(threadID),
+ threadIDMatchesProtocol: (threadID: string) => thickIDRegExp.test(threadID),
allowsDeletingSidebarSource: false,
diff --git a/lib/shared/threads/protocols/farcaster-thread-protocol.js b/lib/shared/threads/protocols/farcaster-thread-protocol.js
--- a/lib/shared/threads/protocols/farcaster-thread-protocol.js
+++ b/lib/shared/threads/protocols/farcaster-thread-protocol.js
@@ -22,7 +22,10 @@
ChangeThreadSettingsPayload,
ThreadJoinPayload,
} from '../../../types/thread-types.js';
-import { pendingThickSidebarURLPrefix } from '../../../utils/validation-utils.js';
+import {
+ farcasterThreadIDRegExp,
+ pendingThickSidebarURLPrefix,
+} from '../../../utils/validation-utils.js';
import { messageNotifyTypes } from '../../messages/message-spec.js';
import type { ThreadProtocol } from '../thread-spec.js';
@@ -129,7 +132,7 @@
},
threadIDMatchesProtocol: (threadID: string): boolean => {
- return threadID.startsWith('FARCASTER#');
+ return farcasterThreadIDRegExp.test(threadID);
},
allowsDeletingSidebarSource: false,
diff --git a/lib/shared/threads/protocols/keyserver-thread-protocol.js b/lib/shared/threads/protocols/keyserver-thread-protocol.js
--- a/lib/shared/threads/protocols/keyserver-thread-protocol.js
+++ b/lib/shared/threads/protocols/keyserver-thread-protocol.js
@@ -69,9 +69,8 @@
SendMessageError,
} from '../../../utils/errors.js';
import {
- isSchemaRegExp,
pendingSidebarURLPrefix,
- thickIDRegex,
+ isKeyserverThreadID,
} from '../../../utils/validation-utils.js';
import { generatePendingThreadColor } from '../../color-utils.js';
import { getNextLocalID } from '../../id-utils.js';
@@ -646,7 +645,7 @@
},
threadIDMatchesProtocol: (threadID: string) => {
- return isSchemaRegExp.test(threadID) && !thickIDRegex.test(threadID);
+ return isKeyserverThreadID(threadID);
},
allowsDeletingSidebarSource: true,
diff --git a/lib/shared/threads/thread-specs.js b/lib/shared/threads/thread-specs.js
--- a/lib/shared/threads/thread-specs.js
+++ b/lib/shared/threads/thread-specs.js
@@ -6,6 +6,8 @@
import { communityRootSpec } from './community-root-spec.js';
import { communitySecretAnnouncementSubthreadSpec } from './community-secret-announcement-subthread-spec.js';
import { communitySecretSubthreadSpec } from './community-secret-subthread-spec.js';
+import { farcasterGroupSpec } from './farcaster-group-spec.js';
+import { farcasterPersonalSpec } from './farcaster-personal-spec.js';
import { genesisPersonalSpec } from './genesis-personal-spec.js';
import { genesisPrivateSpec } from './genesis-private-spec.js';
import { genesisSpec } from './genesis-spec.js';
@@ -38,6 +40,8 @@
[threadTypes.PERSONAL]: personalSpec,
[threadTypes.PRIVATE]: privateSpec,
[threadTypes.THICK_SIDEBAR]: thickSidebarSpec,
+ [threadTypes.FARCASTER_PERSONAL]: farcasterPersonalSpec,
+ [threadTypes.FARCASTER_GROUP]: farcasterGroupSpec,
});
let threadTypesByTrait = null;
diff --git a/lib/types/dm-ops.js b/lib/types/dm-ops.js
--- a/lib/types/dm-ops.js
+++ b/lib/types/dm-ops.js
@@ -29,7 +29,7 @@
import { values } from '../utils/objects.js';
import {
tColor,
- thickIDRegex,
+ thickIDRegExp,
tRegex,
tShape,
tString,
@@ -59,7 +59,7 @@
});
export type DMOperationType = $Values<typeof dmOperationTypes>;
-const tThickID = tRegex(thickIDRegex);
+const tThickID = tRegex(thickIDRegExp);
// In CHANGE_THREAD_SETTINGS operation we're generating message IDs
// based on the prefix that is tThickID. A message with the generated ID
diff --git a/lib/types/message-types.js b/lib/types/message-types.js
--- a/lib/types/message-types.js
+++ b/lib/types/message-types.js
@@ -155,7 +155,7 @@
tNumber,
tShape,
tUserID,
- thickIDRegex,
+ thickIDRegExp,
} from '../utils/validation-utils.js';
const composableMessageTypes = new Set([
@@ -743,5 +743,5 @@
};
export function messageIDIsThick(messageID: string): boolean {
- return thickIDRegex.test(messageID);
+ return thickIDRegExp.test(messageID);
}
diff --git a/lib/types/thread-types-enum.js b/lib/types/thread-types-enum.js
--- a/lib/types/thread-types-enum.js
+++ b/lib/types/thread-types-enum.js
@@ -68,11 +68,18 @@
| NonSidebarThickThreadType
| SidebarThickThreadType;
-export type ThreadType = ThinThreadType | ThickThreadType;
+export const farcasterThreadTypes = Object.freeze({
+ FARCASTER_PERSONAL: 17,
+ FARCASTER_GROUP: 18,
+});
+export type FarcasterThreadType = $Values<typeof farcasterThreadTypes>;
+
+export type ThreadType = ThinThreadType | ThickThreadType | FarcasterThreadType;
export const threadTypes = Object.freeze({
...thinThreadTypes,
...thickThreadTypes,
+ ...farcasterThreadTypes,
});
export function assertThinThreadType(threadType: number): ThinThreadType {
@@ -110,6 +117,19 @@
values(thickThreadTypes),
);
+export function assertFarcasterThreadType(
+ threadType: number,
+): FarcasterThreadType {
+ invariant(
+ threadType === 17 || threadType === 18,
+ 'number is not FarcasterThreadType enum',
+ );
+ return threadType;
+}
+export const farcasterThreadTypeValidator: TRefinement<number> = tNumEnum(
+ values(farcasterThreadTypes),
+);
+
export function assertThreadType(threadType: number): ThreadType {
invariant(
threadType === 3 ||
@@ -125,7 +145,9 @@
threadType === 13 ||
threadType === 14 ||
threadType === 15 ||
- threadType === 16,
+ threadType === 16 ||
+ threadType === 17 ||
+ threadType === 18,
'number is not ThreadType enum',
);
return threadType;
diff --git a/lib/utils/validation-utils.js b/lib/utils/validation-utils.js
--- a/lib/utils/validation-utils.js
+++ b/lib/utils/validation-utils.js
@@ -112,13 +112,27 @@
// PersistentStorageUtilities/ThreadOperationsUtilities/ThreadTypeEnum.h
const uuidRegex =
'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
-const idSchemaRegex = `(?:(?:[0-9]+|${uuidRegex})\\|)?(?:[0-9]+|${uuidRegex})`;
-const isSchemaRegExp: RegExp = new RegExp(`^${idSchemaRegex}$`);
+const thickIDRegExp: RegExp = new RegExp(`^${uuidRegex}$`);
+
+const farcasterThreadIDRegex = 'FARCASTER#(?:(?:[0-9a-z]+)|(?:[0-9]+-[0-9]+))';
+const farcasterThreadIDRegExp: RegExp = new RegExp(
+ `^${farcasterThreadIDRegex}$`,
+);
+
+const idSchemaRegex = `(?:${farcasterThreadIDRegex}|(?:(?:[0-9]+|${uuidRegex})\\|)?(?:[0-9]+|${uuidRegex}))`;
+const idSchemaRegExp: RegExp = new RegExp(`^${idSchemaRegex}$`);
+
+function isKeyserverThreadID(threadID: string): boolean {
+ return (
+ idSchemaRegExp.test(threadID) &&
+ !thickIDRegExp.test(threadID) &&
+ !farcasterThreadIDRegExp.test(threadID)
+ );
+}
const pendingSidebarURLPrefix = 'sidebar';
const pendingThickSidebarURLPrefix = 'dm_sidebar';
const pendingThreadIDRegex = `pending/(type[0-9]+/[0-9]+(\\+[0-9]+)*|(${pendingSidebarURLPrefix}|${pendingThickSidebarURLPrefix})/${idSchemaRegex})`;
-const thickIDRegex: RegExp = new RegExp(`^${uuidRegex}$`);
const chatNameMaxLength = 191;
const chatNameMinLength = 0;
@@ -155,9 +169,11 @@
pendingSidebarURLPrefix,
pendingThickSidebarURLPrefix,
pendingThreadIDRegex,
- thickIDRegex,
+ thickIDRegExp,
validChatNameRegex,
validChatNameRegexString,
chatNameMaxLength,
- isSchemaRegExp,
+ idSchemaRegExp,
+ farcasterThreadIDRegExp,
+ isKeyserverThreadID,
};
diff --git a/lib/utils/validation-utils.test.js b/lib/utils/validation-utils.test.js
--- a/lib/utils/validation-utils.test.js
+++ b/lib/utils/validation-utils.test.js
@@ -1,8 +1,12 @@
// @flow
import {
+ idSchemaRegExp,
tMediaMessagePhoto,
tMediaMessageVideo,
+ isKeyserverThreadID,
+ thickIDRegExp,
+ farcasterThreadIDRegExp,
tNumEnum,
} from './validation-utils.js';
import { threadTypes } from '../types/thread-types-enum.js';
@@ -139,4 +143,149 @@
).toBe(false);
});
});
+
+ describe('ID regex tests', () => {
+ describe('Keyserver ID regex', () => {
+ it('Should match compound IDs with keyserver components', () => {
+ expect(isKeyserverThreadID('256|123')).toBe(true);
+ expect(isKeyserverThreadID('123|456')).toBe(true);
+ expect(
+ isKeyserverThreadID('550e8400-e29b-41d4-a716-446655440000|456'),
+ ).toBe(true);
+ });
+
+ it('Should not match thick IDs (UUIDs)', () => {
+ expect(
+ isKeyserverThreadID('550e8400-e29b-41d4-a716-446655440000'),
+ ).toBe(false);
+ });
+
+ it('Should not match Farcaster IDs', () => {
+ expect(isKeyserverThreadID('FARCASTER#ef5a742bca')).toBe(false);
+ expect(isKeyserverThreadID('FARCASTER#12345-635')).toBe(false);
+ });
+
+ it('Should not match invalid formats', () => {
+ expect(isKeyserverThreadID('')).toBe(false);
+ expect(isKeyserverThreadID('abc')).toBe(false);
+ expect(isKeyserverThreadID('123abc')).toBe(false);
+ });
+ });
+
+ describe('Thick ID regex', () => {
+ it('Should match valid UUIDs', () => {
+ expect(thickIDRegExp.test('550e8400-e29b-41d4-a716-446655440000')).toBe(
+ true,
+ );
+ expect(thickIDRegExp.test('6ba7b810-9dad-11d1-80b4-00c04fd430c8')).toBe(
+ true,
+ );
+ expect(thickIDRegExp.test('12345678-1234-1234-1234-123456789abc')).toBe(
+ true,
+ );
+ });
+
+ it('Should not match keyserver IDs', () => {
+ expect(thickIDRegExp.test('256')).toBe(false);
+ expect(thickIDRegExp.test('1234567890')).toBe(false);
+ });
+
+ it('Should not match Farcaster IDs', () => {
+ expect(thickIDRegExp.test('FARCASTER#ef5a742bca')).toBe(false);
+ expect(thickIDRegExp.test('FARCASTER#12345-635')).toBe(false);
+ });
+
+ it('Should not match malformed UUIDs', () => {
+ expect(thickIDRegExp.test('550e8400-e29b-41d4-a716-44665544000')).toBe(
+ false,
+ );
+ expect(
+ thickIDRegExp.test('550e8400-e29b-41d4-a716-446655440000-extra'),
+ ).toBe(false);
+ expect(thickIDRegExp.test('550e8400-e29b-41d4-a716')).toBe(false);
+ });
+ });
+
+ describe('Farcaster ID regex', () => {
+ it('Should match group conversation', () => {
+ expect(farcasterThreadIDRegExp.test('FARCASTER#ef5a742bca')).toBe(true);
+ expect(farcasterThreadIDRegExp.test('FARCASTER#abc123')).toBe(true);
+ expect(farcasterThreadIDRegExp.test('FARCASTER#0123456789abcdef')).toBe(
+ true,
+ );
+ });
+
+ it('Should match 1:1 conversation', () => {
+ expect(farcasterThreadIDRegExp.test('FARCASTER#12345-635')).toBe(true);
+ expect(farcasterThreadIDRegExp.test('FARCASTER#1-2')).toBe(true);
+ expect(farcasterThreadIDRegExp.test('FARCASTER#111111-999999')).toBe(
+ true,
+ );
+ });
+
+ it('Should not match keyserver IDs', () => {
+ expect(farcasterThreadIDRegExp.test('256')).toBe(false);
+ expect(farcasterThreadIDRegExp.test('1234567890')).toBe(false);
+ });
+
+ it('Should not match thick IDs', () => {
+ expect(
+ farcasterThreadIDRegExp.test('550e8400-e29b-41d4-a716-446655440000'),
+ ).toBe(false);
+ });
+
+ it('Should not match malformed Farcaster IDs', () => {
+ expect(farcasterThreadIDRegExp.test('farcaster')).toBe(false);
+ expect(farcasterThreadIDRegExp.test('FARCASTER#')).toBe(false);
+ expect(farcasterThreadIDRegExp.test('notFARCASTER#12345')).toBe(false);
+ });
+ });
+
+ describe('idSchemaRegex', () => {
+ it('Should match keyserver IDs', () => {
+ expect(idSchemaRegExp.test('256')).toBe(true);
+ expect(idSchemaRegExp.test('1234567890')).toBe(true);
+ expect(idSchemaRegExp.test('1')).toBe(true);
+ });
+
+ it('Should match thick IDs', () => {
+ expect(
+ idSchemaRegExp.test('550e8400-e29b-41d4-a716-446655440000'),
+ ).toBe(true);
+ expect(
+ idSchemaRegExp.test('6ba7b810-9dad-11d1-80b4-00c04fd430c8'),
+ ).toBe(true);
+ });
+
+ it('Should match Farcaster IDs', () => {
+ expect(idSchemaRegExp.test('FARCASTER#ef5a742bca')).toBe(true);
+ expect(idSchemaRegExp.test('FARCASTER#12345-635')).toBe(true);
+ });
+
+ it('Should match compound IDs with keyserver prefix', () => {
+ expect(
+ idSchemaRegExp.test('256|550e8400-e29b-41d4-a716-446655440000'),
+ ).toBe(true);
+ });
+
+ it('Should match compound IDs with UUID prefix', () => {
+ expect(
+ idSchemaRegExp.test('550e8400-e29b-41d4-a716-446655440000|256'),
+ ).toBe(true);
+ });
+
+ it('Should not match invalid formats', () => {
+ expect(idSchemaRegExp.test('')).toBe(false);
+ expect(idSchemaRegExp.test('invalid')).toBe(false);
+ expect(idSchemaRegExp.test('123abc')).toBe(false);
+ expect(idSchemaRegExp.test('farcaster')).toBe(false);
+ expect(idSchemaRegExp.test('550e8400-e29b-41d4-a716')).toBe(false);
+ });
+
+ it('Should not match multiple pipe separators', () => {
+ expect(idSchemaRegExp.test('256|123|456')).toBe(false);
+ expect(idSchemaRegExp.test('256||123')).toBe(false);
+ });
+ });
+ });
});
diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
--- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
+++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.h
@@ -29,7 +29,6 @@
std::vector<MessageEntity>
processMessagesResults(SQLiteStatementWrapper &preparedSQL) const;
- std::string getThickThreadTypesList() const;
public:
SQLiteQueryExecutor(std::string sqliteFilePath, bool skipMigration = false);
diff --git a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
--- a/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
+++ b/native/cpp/CommonCpp/DatabaseManagers/SQLiteQueryExecutor.cpp
@@ -188,20 +188,6 @@
}
}
-std::string SQLiteQueryExecutor::getThickThreadTypesList() const {
- std::stringstream resultStream;
- for (auto it = THICK_THREAD_TYPES.begin(); it != THICK_THREAD_TYPES.end();
- ++it) {
- int typeInt = static_cast<int>(*it);
- resultStream << typeInt;
-
- if (it + 1 != THICK_THREAD_TYPES.end()) {
- resultStream << ",";
- }
- }
- return resultStream.str();
-}
-
std::vector<MessageEntity> SQLiteQueryExecutor::getInitialMessages() const {
static std::string getInitialMessagesSQL =
"SELECT "
diff --git a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/ThreadOperationsUtilities/ThreadTypeEnum.h b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/ThreadOperationsUtilities/ThreadTypeEnum.h
--- a/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/ThreadOperationsUtilities/ThreadTypeEnum.h
+++ b/native/cpp/CommonCpp/NativeModules/PersistentStorageUtilities/ThreadOperationsUtilities/ThreadTypeEnum.h
@@ -22,35 +22,41 @@
PERSONAL = 14,
PRIVATE = 15,
THICK_SIDEBAR = 16,
+ FARCASTER_PERSONAL = 17,
+ FARCASTER_GROUP = 18,
};
-const std::vector<ThreadType> THICK_THREAD_TYPES{
- ThreadType::LOCAL,
- ThreadType::PERSONAL,
- ThreadType::PRIVATE,
- ThreadType::THICK_SIDEBAR};
-
// Regex patterns - should be in sync with lib/utils/validation-utils.js
const std::string UUID_REGEX_STRING =
"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{"
"12}";
-const std::string ID_SCHEMA_REGEX_STRING = "(?:(?:[0-9]+|" + UUID_REGEX_STRING +
- ")\\|)?(?:[0-9]+|" + UUID_REGEX_STRING + ")";
-
-const std::regex IS_SCHEMA_REGEX("^" + ID_SCHEMA_REGEX_STRING + "$");
+const std::string FARCASTER_ID_REGEX_STRING =
+ "FARCASTER#(?:(?:[0-9a-z]+)|(?:"
+ "[0-9]+-[0-9]+))";
+const std::string ID_SCHEMA_REGEX_STRING = "(?:" + FARCASTER_ID_REGEX_STRING +
+ "|(?:(?:[0-9]+|" + UUID_REGEX_STRING + ")\\|)?(?:[0-9]+|" +
+ UUID_REGEX_STRING + "))";
+
+const std::regex ID_SCHEMA_REGEX("^" + ID_SCHEMA_REGEX_STRING + "$");
const std::regex THICK_ID_REGEX("^" + UUID_REGEX_STRING + "$");
+const std::regex FARCASTER_ID_REGEX("^" + FARCASTER_ID_REGEX_STRING + "$");
// Helper functions for regex testing
inline bool isSchemaID(const std::string &threadID) {
- return std::regex_match(threadID, IS_SCHEMA_REGEX);
+ return std::regex_match(threadID, ID_SCHEMA_REGEX);
}
inline bool isThickID(const std::string &threadID) {
return std::regex_match(threadID, THICK_ID_REGEX);
}
+inline bool isFarcasterID(const std::string &threadID) {
+ return std::regex_match(threadID, FARCASTER_ID_REGEX);
+}
+
inline bool threadIDMatchesKeyserverProtocol(const std::string &threadID) {
- return isSchemaID(threadID) && !isThickID(threadID);
+ return isSchemaID(threadID) && !isThickID(threadID) &&
+ !isFarcasterID(threadID);
}
} // namespace comm
diff --git a/web/shared-worker/_generated/comm_query_executor.wasm b/web/shared-worker/_generated/comm_query_executor.wasm
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
literal 0
Hc$@<O00001
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Dec 6, 6:08 PM (20 h, 58 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5839301
Default Alt Text
D15185.1765044518.diff (18 KB)
Attached To
Mode
D15185: [lib] Introduce Farcaster thread IDs and thread types
Attached
Detach File
Event Timeline
Log In to Comment