newOS for Developers
Folder / Mood Actions
Complete API for creating, reading, and managing folders (moods/spaces) — the primary content containers in newOS.
packages/newgraph-signals/src/actions/folder.ts (1166 lines)
Quick Reference
CRUD Operations
readFolder— Fetch folder by IDcreateFolder— Create new folderupdateFolder— Update folder propsreadMultipleFolder— Fetch multiple foldersdownloadFolder— Export folder as JSON
Post Management
readPosts— Get folder's postsattachToFolder— Attach postmassAttachToFolder— Bulk attachattachToFolders— Post to multiple folderscachePostAttachment— Cache edges
Access Control
requestAccess— Request accessgrantWriteAccess— Grant to usergrantWriteAccessMulti— Grant to multiplereadFolderGrantees— List granteescacheGrants— Cache grant edges
Cache Queries
folderQuery— Get from cachefoldersQuery— Get multiplefolderPosts— Cached postsgrantees— Cached granteestopFolders— Public folders
Discovery
readTopFolders— List top public foldersgetOneOnOneFolder— DM folderliveSubscribe— WebSocket updates
Caching Utilities
cacheFolders— Store to IndexedDBcacheFoldersBatchAsync— Batched caching
readFolder
CRUDFetch a folder/mood by ID. Returns cached data first, then fetches from API.
readFolder({
id: string; // Required - Folder ID
preferPublic?: boolean; // Optional - Use public endpoint
}): ProgressiveHandlerResponse<EnrichedFolder>| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Folder/mood ID |
| preferPublic | boolean | No | If true, uses public endpoint even when logged in |
API Endpoints
api.mood.readPrivateList({ id }) — When logged inapi.mood.moodList({ id }) — Public endpointNuances
- Auto-starts on call (no need to call
.exec()) - Caches folder and its posts to IndexedDB
- Sets
accessLevelto empty string if missing - HTTP 403 throws error, not cached as private
- Uses
_current.value.idto determine if user is logged in
Usage
const [folder, progress] = readFolder({ id: "abc123" });
// Auto-starts, no exec needed
await progress.value.promise;
console.log(folder.value); // EnrichedFolderreadMultipleFolder
CRUDFetch multiple folders in parallel by their IDs.
readMultipleFolder({
ids: string[]; // Required - Array of folder IDs
}): ProgressiveHandlerResponse<EnrichedFolder[]>API Endpoint
api.mood.moodList({ id }) — Called for each ID in parallelNuances
- Auto-starts on call
- Uses
Promise.allfor parallel fetching - Caches each folder and its posts individually
- Returns cached data first via
foldersQuery(ids)
Usage
const [folders, progress] = readMultipleFolder({
ids: ["folder1", "folder2", "folder3"]
});
await progress.value.promise;
console.log(folders.value); // EnrichedFolder[]readPosts
CRUDFetch all posts attached to a folder with sorting and filtering.
readPosts({
id: string; // Required - Folder ID
sortBy?: string; // Optional - Sort field (default: "created")
reverse?: boolean; // Optional - Desc order (default: true)
noTextNodes?: boolean; // Optional - Exclude text-only posts
noPrivate?: boolean; // Optional - Exclude private posts
loadAll?: boolean; // Optional - Paginate through all (default: true)
access?: "private" | "public" | "auto"; // Optional
}, opts?: { readAll: boolean }): ProgressiveHandlerResponse<EnrichedPost[]>| Parameter | Type | Default | Description |
|---|---|---|---|
| id | string | — | Folder ID (required) |
| sortBy | string | "created" | Sort field |
| reverse | boolean | true | Descending order |
| noTextNodes | boolean | false | Exclude text/plain posts |
| noPrivate | boolean | false | Exclude private posts |
| loadAll | boolean | true | Auto-paginate all pages |
| access | string | "auto" | Force private/public endpoint |
Sorting Options
created— Default, by creation dateusers— By author watts, then rating (advanced)points— Group by author, sum ratings (advanced)
API Endpoints
api.mood.attachmentsList({ id, page, sortBy, order }) — When logged inapi.mood.attachmentsPublicList({ id, page, sortBy, order }) — PublicNuances
- Computes
relativeRatingfor posts with /card content - Filters
deletedposts automatically - Stores edges:
folder+id+attachment+post - Default page size: 50 (or 1000 if readAll)
- Supports
-text/plaincontentType filter via query param
createFolder
CRUDCreate a new folder/mood. Supports "future" folders (local-only until committed).
createFolder({
key?: string; // Optional - Deduplication key
}): ProgressiveHandlerResponse<MoodCreateResponse[]>
// Execute with:
progress.exec({
id?: string; // Optional - Pre-set ID
title?: string;
description?: string;
isPrivate?: boolean; // Default: true
licenseType?: string; // Default: "private" if future
flow?: string; // AI flow settings
defaultView?: string; // e.g., "chat"
future?: boolean; // Local-only until committed
futureGrantees?: { id: string }[]; // Grant access on commit
failIfExists?: boolean; // Throw if duplicate
ignoreExistingFuture?: boolean; // Skip future check
})Future Folders
When future: true, the folder is stored locally with a UUID. No API call is made. The folder is committed to the API when you call again with future: false.
API Endpoint
api.mood.moodCreate(folderForm)Flow Flags
web— Standard web folderswarm— Collaborative AI folderblank— No AI flow, used for DM chats
Nuances
- Generates UUID via
uuidv4()for future folders - Auto-grants access to
futureGranteeson commit - Sets author to
_current.value - Has
onResetcallback to clear response signal
updateFolder
CRUDUpdate folder properties. Auto-commits future folders.
updateFolder(): ProgressiveHandlerResponse<MoodReadResponse[]>
// Execute with:
progress.exec({
id: string; // Required - Folder ID
title?: string;
description?: string;
flow?: string;
isPrivate?: boolean;
future?: boolean; // Keep as future folder
})API Endpoint
api.mood.moodUpdate(folderForm)Nuances
- If cached folder is future and form is not, commits to API first via
createFolder - Updates cache in-place with
.modify() - Appends to response signal array
attachToFolder
AttachmentAttach a post to a folder. Creates the edge relationship.
attachToFolder(
post: PostReadResponse,
folder: MoodReadResponse
): Promise<void>API Endpoint
api.mood.attachPostUpdate({ targetId: post.id, id: folder.id })Related Functions
massAttachToFolder(posts, folder)— Attach multiple posts sequentially to one folderattachToFolders(post, folders)— Attach one post to multiple folders in parallelattachToFolderProgressive()— ProgressiveHandler variant with progress trackingattachToFolderProgressiveMulti()— Batch attach with progress
Attacheable Labels
post—api.mood.attachPostUpdatemood—api.mood.attachMoodUpdateuser—api.mood.attachUserUpdate
requestAccess
Access ControlRequest access to a private folder.
requestAccess({
folder: MoodReadResponse;
}): Promise<void>API Endpoint
api.mood.accessRequestCreate({ targetId: folder.id })Nuances
- Updates cache with
accessLevel: "request" - Creates edge:
user+id+access+folderwith level "request" - Uses current user from
_current.value
grantWriteAccess
Access ControlGrant a user access to a folder.
grantWriteAccess({
user: UserReadPublicResponse;
folder: MoodReadResponse;
accessLevel?: string; // Default: "write"
}): Promise<void>API Endpoint
api.mood.accessGrantCreate({ targetId, grantee, policy })grantWriteAccessMulti
ProgressiveHandler variant for granting access to multiple users/folders. Handles future folder scenarios.
grantWriteAccessMulti(): ProgressiveHandlerResponse<{ redirect: string }>
// Execute with:
progress.exec({
users: UserReadPublicResponse[];
folders: EnrichedFolder[];
ignoreOneOnOne?: boolean; // Convert oneOnOne to regular folder
})readFolderGrantees
Access ControlList all users with access to a folder.
readFolderGrantees({
id: string; // Folder ID
}): ProgressiveHandlerResponse<UserReadPublicResponseWithAccess[]>API Endpoint
api.mood.accessGranteesList({ id, page })Nuances
- Auto-starts with 1.5s delay for debouncing
- Uses
cacheGrantsto store edges - Paginated response
readTopFolders
DiscoveryFetch paginated list of top public folders.
readTopFolders(params?: {
page?: number;
}): ProgressiveHandlerResponse<EnrichedFolder[]>API Endpoint
api.mood.listTopList({ page })getOneOnOneFolder
SpecialGet or create a direct message folder between two users.
getOneOnOneFolder({
id?: string; // Target user ID
username?: string; // Target username
}): ProgressiveHandlerResponse<EnrichedFolder>ID Construction
OneOnOne folder ID = [userId1, userId2].sort().join("_")
API Endpoint
api.mood.oneononeList({ id })Nuances
- Chat title format:
username1 - username2 - Sets
defaultView: "chat"andflow: "blank" - Auto-triggers
readFolderGranteesfor the new folder
liveSubscribe
Real-timeSubscribe to real-time updates for a folder via WebSocket.
liveSubscribe(folderId: string): Promise<void>WebSocket Message
{ action: "subscribe", targetId: folderId }Nuances
- Only subscribes once per folder (tracked in
subscribeddictionary) - Uses
newgraphWebsocketsClientfor connection
downloadFolder
ExportExport a folder's contents as a JSON file download.
downloadFolder({
folderId: string;
}): ProgressiveHandlerResponse<null>Nuances
- Calls
readPostswithloadAll: true - Downloads as
folderId_folder_data_timestamp.json - Does not auto-start (must call
exec())
Cache Queries
IndexedDBQuery cached data without API calls.
folderQuery / foldersQuery
folderQuery(id: string): Promise<EnrichedFolder>
foldersQuery(id: string | string[]): Promise<EnrichedFolder[]>Returns folders from cache.folder table, sorted by created date ascending.
folderPosts
folderPosts(id: string, opts?: {
sortBy?: string;
reverse?: boolean;
noTextNodes?: boolean;
noPrivate?: boolean;
}): Promise<EnrichedPost[]>Uses edge table: folder+id+attachment+post. Supports advanced sorting modes.
grantees
grantees(id: string, opts?: {
sortBy?: string;
reverse?: boolean;
accessLevel?: string;
}): Promise<UserReadPublicResponseWithAccess[]>Uses edge table: folder+id+access+user. Returns futureGrantees for future folders.
topFolders
topFolders(id?: string, opts?: {
sortBy?: string;
reverse?: boolean;
}): Promise<EnrichedFolder[]>Filters out private folders from cache.
Caching Functions
IndexedDBcacheFolders
cacheFolders(
folders: EnrichedFolder | EnrichedFolder[],
forceOrOpts?: boolean | { force?: boolean; appendProps?: string[] }
): Promise<void>Stores folders to IndexedDB. Merges with existing cached data using mergeObjects. Uses bulkPut for multiple folders.
cacheFoldersBatchAsync
cacheFoldersBatchAsync: BatchedFunction
// Batches: 100 items, 10s max retentionBatched version for high-throughput caching.
cachePostAttachment
cachePostAttachment(
posts: PostReadResponse[],
folders: MoodReadResponse[],
relName?: string | string[] // Default: "attachment"
): Promise<void>Creates edges for post-folder relationships. Uses delayed edge storage.
cacheGrants
cacheGrants(
folderId: string,
grants: MoodListGranteesResponse
): Promise<void>Caches grantee users and their access edges with level and updated timestamp.
Edge Table Reference
| Pattern | Relationship |
|---|---|
| folder+{id}+attachment+post | Post attached to folder |
| folder+{id}+access+user | User has access to folder |
| user+{id}+author+folder | User authored folder |
URL Construction
/api/mood/{ id }/api/mood/{ id }/attachments?page=0&sortBy=created&order=descPOST /api/mood/access/grantPOST /api/mood/access/request/api/mood/top?page=0/api/mood/oneonone/{ userId }Naming: moodId vs folderId
Historical context: In the API, folders are called "moods" (legacy naming). The client-side code uses both terms interchangeably:
moodId— Used in API responses/requestsfolderId— Used in some client functionsMoodReadResponsevsEnrichedFolder— Same structure