@nurama/sdk 0.0.0-stage → 1.4.1
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/LICENSE +202 -0
- package/NOTICE +5 -0
- package/README.md +1080 -2
- package/dist/BotClient.d.ts +66 -0
- package/dist/BotClient.d.ts.map +1 -0
- package/dist/BotClient.js +68 -0
- package/dist/BotClient.js.map +1 -0
- package/dist/NuramaClient.d.ts +480 -0
- package/dist/NuramaClient.d.ts.map +1 -0
- package/dist/NuramaClient.js +902 -0
- package/dist/NuramaClient.js.map +1 -0
- package/dist/browser/nurama-bot-sdk.js +12051 -0
- package/dist/browser/nurama-bot-sdk.min.js +1 -0
- package/dist/browser/nurama-sdk.js +12003 -0
- package/dist/browser/nurama-sdk.min.js +1 -0
- package/dist/routes/ai.d.ts +280 -0
- package/dist/routes/ai.d.ts.map +1 -0
- package/dist/routes/ai.js +173 -0
- package/dist/routes/ai.js.map +1 -0
- package/dist/routes/asset.d.ts +493 -0
- package/dist/routes/asset.d.ts.map +1 -0
- package/dist/routes/asset.js +848 -0
- package/dist/routes/asset.js.map +1 -0
- package/dist/routes/auth.d.ts +218 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +454 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/blogPosts.d.ts +17 -0
- package/dist/routes/blogPosts.d.ts.map +1 -0
- package/dist/routes/blogPosts.js +29 -0
- package/dist/routes/blogPosts.js.map +1 -0
- package/dist/routes/board.d.ts +187 -0
- package/dist/routes/board.d.ts.map +1 -0
- package/dist/routes/board.js +270 -0
- package/dist/routes/board.js.map +1 -0
- package/dist/routes/bot.d.ts +202 -0
- package/dist/routes/bot.d.ts.map +1 -0
- package/dist/routes/bot.js +229 -0
- package/dist/routes/bot.js.map +1 -0
- package/dist/routes/chat.d.ts +842 -0
- package/dist/routes/chat.d.ts.map +1 -0
- package/dist/routes/chat.js +863 -0
- package/dist/routes/chat.js.map +1 -0
- package/dist/routes/chatAi.d.ts +51 -0
- package/dist/routes/chatAi.d.ts.map +1 -0
- package/dist/routes/chatAi.js +109 -0
- package/dist/routes/chatAi.js.map +1 -0
- package/dist/routes/config.d.ts +11 -0
- package/dist/routes/config.d.ts.map +1 -0
- package/dist/routes/config.js +24 -0
- package/dist/routes/config.js.map +1 -0
- package/dist/routes/convo.d.ts +169 -0
- package/dist/routes/convo.d.ts.map +1 -0
- package/dist/routes/convo.js +284 -0
- package/dist/routes/convo.js.map +1 -0
- package/dist/routes/credits.d.ts +82 -0
- package/dist/routes/credits.d.ts.map +1 -0
- package/dist/routes/credits.js +49 -0
- package/dist/routes/credits.js.map +1 -0
- package/dist/routes/device.d.ts +74 -0
- package/dist/routes/device.d.ts.map +1 -0
- package/dist/routes/device.js +122 -0
- package/dist/routes/device.js.map +1 -0
- package/dist/routes/folder.d.ts +75 -0
- package/dist/routes/folder.d.ts.map +1 -0
- package/dist/routes/folder.js +99 -0
- package/dist/routes/folder.js.map +1 -0
- package/dist/routes/invite.d.ts +61 -0
- package/dist/routes/invite.d.ts.map +1 -0
- package/dist/routes/invite.js +86 -0
- package/dist/routes/invite.js.map +1 -0
- package/dist/routes/joinLink.d.ts +88 -0
- package/dist/routes/joinLink.d.ts.map +1 -0
- package/dist/routes/joinLink.js +205 -0
- package/dist/routes/joinLink.js.map +1 -0
- package/dist/routes/membership.d.ts +116 -0
- package/dist/routes/membership.d.ts.map +1 -0
- package/dist/routes/membership.js +183 -0
- package/dist/routes/membership.js.map +1 -0
- package/dist/routes/notification.d.ts +103 -0
- package/dist/routes/notification.d.ts.map +1 -0
- package/dist/routes/notification.js +89 -0
- package/dist/routes/notification.js.map +1 -0
- package/dist/routes/oauthGrant.d.ts +45 -0
- package/dist/routes/oauthGrant.d.ts.map +1 -0
- package/dist/routes/oauthGrant.js +32 -0
- package/dist/routes/oauthGrant.js.map +1 -0
- package/dist/routes/payment.d.ts +56 -0
- package/dist/routes/payment.d.ts.map +1 -0
- package/dist/routes/payment.js +78 -0
- package/dist/routes/payment.js.map +1 -0
- package/dist/routes/product.d.ts +43 -0
- package/dist/routes/product.d.ts.map +1 -0
- package/dist/routes/product.js +53 -0
- package/dist/routes/product.js.map +1 -0
- package/dist/routes/project.d.ts +821 -0
- package/dist/routes/project.d.ts.map +1 -0
- package/dist/routes/project.js +1153 -0
- package/dist/routes/project.js.map +1 -0
- package/dist/routes/public.d.ts +269 -0
- package/dist/routes/public.d.ts.map +1 -0
- package/dist/routes/public.js +412 -0
- package/dist/routes/public.js.map +1 -0
- package/dist/routes/scratch.d.ts +70 -0
- package/dist/routes/scratch.d.ts.map +1 -0
- package/dist/routes/scratch.js +67 -0
- package/dist/routes/scratch.js.map +1 -0
- package/dist/routes/settings.d.ts +102 -0
- package/dist/routes/settings.d.ts.map +1 -0
- package/dist/routes/settings.js +94 -0
- package/dist/routes/settings.js.map +1 -0
- package/dist/routes/shortlink.d.ts +79 -0
- package/dist/routes/shortlink.d.ts.map +1 -0
- package/dist/routes/shortlink.js +25 -0
- package/dist/routes/shortlink.js.map +1 -0
- package/dist/routes/socket.d.ts +108 -0
- package/dist/routes/socket.d.ts.map +1 -0
- package/dist/routes/socket.js +573 -0
- package/dist/routes/socket.js.map +1 -0
- package/dist/routes/storage.d.ts +44 -0
- package/dist/routes/storage.d.ts.map +1 -0
- package/dist/routes/storage.js +49 -0
- package/dist/routes/storage.js.map +1 -0
- package/dist/routes/subscription.d.ts +184 -0
- package/dist/routes/subscription.d.ts.map +1 -0
- package/dist/routes/subscription.js +219 -0
- package/dist/routes/subscription.js.map +1 -0
- package/dist/routes/supportChat.d.ts +40 -0
- package/dist/routes/supportChat.d.ts.map +1 -0
- package/dist/routes/supportChat.js +53 -0
- package/dist/routes/supportChat.js.map +1 -0
- package/dist/routes/supportTicket.d.ts +89 -0
- package/dist/routes/supportTicket.d.ts.map +1 -0
- package/dist/routes/supportTicket.js +54 -0
- package/dist/routes/supportTicket.js.map +1 -0
- package/dist/routes/tag.d.ts +72 -0
- package/dist/routes/tag.d.ts.map +1 -0
- package/dist/routes/tag.js +81 -0
- package/dist/routes/tag.js.map +1 -0
- package/dist/routes/task.d.ts +252 -0
- package/dist/routes/task.d.ts.map +1 -0
- package/dist/routes/task.js +284 -0
- package/dist/routes/task.js.map +1 -0
- package/dist/routes/taskRelation.d.ts +80 -0
- package/dist/routes/taskRelation.d.ts.map +1 -0
- package/dist/routes/taskRelation.js +71 -0
- package/dist/routes/taskRelation.js.map +1 -0
- package/dist/routes/token.d.ts +97 -0
- package/dist/routes/token.d.ts.map +1 -0
- package/dist/routes/token.js +73 -0
- package/dist/routes/token.js.map +1 -0
- package/dist/routes/user.d.ts +112 -0
- package/dist/routes/user.d.ts.map +1 -0
- package/dist/routes/user.js +151 -0
- package/dist/routes/user.js.map +1 -0
- package/dist/routes/version.d.ts +42 -0
- package/dist/routes/version.d.ts.map +1 -0
- package/dist/routes/version.js +38 -0
- package/dist/routes/version.js.map +1 -0
- package/dist/routes/webhook.d.ts +170 -0
- package/dist/routes/webhook.d.ts.map +1 -0
- package/dist/routes/webhook.js +173 -0
- package/dist/routes/webhook.js.map +1 -0
- package/dist/routes/workspace.d.ts +120 -0
- package/dist/routes/workspace.d.ts.map +1 -0
- package/dist/routes/workspace.js +199 -0
- package/dist/routes/workspace.js.map +1 -0
- package/dist/utils/uploadSessionManager.d.ts +133 -0
- package/dist/utils/uploadSessionManager.d.ts.map +1 -0
- package/dist/utils/uploadSessionManager.js +321 -0
- package/dist/utils/uploadSessionManager.js.map +1 -0
- package/dist/utils/urlParams.d.ts +35 -0
- package/dist/utils/urlParams.d.ts.map +1 -0
- package/dist/utils/urlParams.js +146 -0
- package/dist/utils/urlParams.js.map +1 -0
- package/dist/version.d.ts +15 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +87 -3
- package/src/BotClient.ts +113 -0
- package/src/NuramaClient.ts +1253 -0
- package/src/bot-browser-entry.js +15 -0
- package/src/browser-entry.js +20 -0
- package/src/routes/ai.ts +378 -0
- package/src/routes/asset.ts +1104 -0
- package/src/routes/auth.ts +587 -0
- package/src/routes/blogPosts.ts +29 -0
- package/src/routes/board.ts +403 -0
- package/src/routes/bot.ts +356 -0
- package/src/routes/chat.ts +1292 -0
- package/src/routes/chatAi.ts +125 -0
- package/src/routes/config.ts +31 -0
- package/src/routes/convo.ts +321 -0
- package/src/routes/credits.ts +112 -0
- package/src/routes/device.ts +133 -0
- package/src/routes/folder.ts +154 -0
- package/src/routes/invite.ts +133 -0
- package/src/routes/joinLink.ts +233 -0
- package/src/routes/membership.ts +237 -0
- package/src/routes/notification.ts +166 -0
- package/src/routes/oauthGrant.ts +64 -0
- package/src/routes/payment.ts +104 -0
- package/src/routes/product.ts +67 -0
- package/src/routes/project.ts +1528 -0
- package/src/routes/public.ts +496 -0
- package/src/routes/scratch.ts +94 -0
- package/src/routes/settings.ts +152 -0
- package/src/routes/shortlink.ts +90 -0
- package/src/routes/socket.ts +757 -0
- package/src/routes/storage.ts +83 -0
- package/src/routes/subscription.ts +307 -0
- package/src/routes/supportChat.ts +62 -0
- package/src/routes/supportTicket.ts +114 -0
- package/src/routes/tag.ts +131 -0
- package/src/routes/task.ts +431 -0
- package/src/routes/taskRelation.ts +125 -0
- package/src/routes/token.ts +152 -0
- package/src/routes/user.ts +214 -0
- package/src/routes/version.ts +62 -0
- package/src/routes/webhook.ts +295 -0
- package/src/routes/workspace.ts +223 -0
- package/src/utils/uploadSessionManager.ts +407 -0
- package/src/utils/urlParams.ts +181 -0
- package/src/version.ts +22 -0
package/README.md
CHANGED
|
@@ -1,3 +1,1081 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Nurama SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Provides API, web-socket, and additional utilities for interfacing with the Nurama platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install nurama-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Basic API Usage
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
import NuramaClient from 'nurama-sdk';
|
|
17
|
+
|
|
18
|
+
// Create client instance
|
|
19
|
+
const client = new NuramaClient('https://api.nurama.com', {
|
|
20
|
+
debug: true, // Optional: Enable debug logging
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Login to get token
|
|
24
|
+
await client.auth.login({
|
|
25
|
+
email: 'user@example.com',
|
|
26
|
+
password: 'yourpassword',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Make API calls using the client
|
|
30
|
+
const workspaces = await client.workspace.listWorkspaces();
|
|
31
|
+
console.log(workspaces);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### WebSocket Support
|
|
35
|
+
|
|
36
|
+
The SDK provides WebSocket functionality for real-time notifications and events. Socket.IO is used under the hood, which handles protocol conversion automatically.
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import NuramaClient from 'nurama-sdk';
|
|
40
|
+
|
|
41
|
+
// Create client instance with a separate WebSocket server URL if needed
|
|
42
|
+
const client = new NuramaClient('https://api.nurama.com', {
|
|
43
|
+
websocketURL: 'https://ws.nurama.com', // Optional: Can use HTTP/HTTPS URLs (Socket.IO handles protocol)
|
|
44
|
+
browserMode: false, // Set to true for browser environments that need localStorage
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Login first to authenticate
|
|
48
|
+
await client.auth.login({
|
|
49
|
+
email: 'user@example.com',
|
|
50
|
+
password: 'yourpassword',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Connect to a user channel
|
|
54
|
+
const userId = '123456789';
|
|
55
|
+
await client.socket.connect(`/user/${userId}`);
|
|
56
|
+
|
|
57
|
+
// Connect to a channel with a different WebSocket URL just for this connection
|
|
58
|
+
await client.socket.connect('/workspace/456', {
|
|
59
|
+
websocketURL: 'https://alt-ws.nurama.com' // Can use HTTP/HTTPS URLs
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Subscribe to notifications
|
|
63
|
+
await client.socket.subscribe(`/user/${userId}`, 'notification', (event) => {
|
|
64
|
+
console.log('Received notification:', event);
|
|
65
|
+
|
|
66
|
+
// Handle different notification types
|
|
67
|
+
switch (event.type) {
|
|
68
|
+
case 'workspaceCreate':
|
|
69
|
+
console.log('New workspace created:', event.changes?.create?.[0]?.resource);
|
|
70
|
+
break;
|
|
71
|
+
case 'projectUpdate':
|
|
72
|
+
console.log('Project updated:', event.changes?.update?.[0]?.resource);
|
|
73
|
+
break;
|
|
74
|
+
// Handle other event types
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Disconnect when done
|
|
79
|
+
await client.socket.disconnect(`/user/${userId}`);
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
#### WebSocket Methods
|
|
83
|
+
|
|
84
|
+
- `connect(channel, options)`: Connect to a WebSocket channel
|
|
85
|
+
- `disconnect(channel)`: Disconnect from a channel
|
|
86
|
+
- `disconnectAll()`: Disconnect from all channels
|
|
87
|
+
- `subscribe(channel, event, callback)`: Subscribe to an event on a channel
|
|
88
|
+
- `unsubscribe(channel, event, callback?)`: Unsubscribe from an event
|
|
89
|
+
- `isConnected(channel)`: Check if connected to a channel
|
|
90
|
+
|
|
91
|
+
#### Connection Options
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const options = {
|
|
95
|
+
autoRefresh: true, // Auto refresh token when expired
|
|
96
|
+
autoReconnect: true, // Auto reconnect on disconnection
|
|
97
|
+
autoReconnectOnTokenExpiry: true, // Auto reconnect when token expires
|
|
98
|
+
debug: false, // Enable debug logging
|
|
99
|
+
websocketURL: 'https://custom-ws.nurama.com' // Override WebSocket URL for this connection
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
await client.socket.connect('/user/123', options);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Documentation
|
|
106
|
+
|
|
107
|
+
For detailed API documentation, see the [API Reference](https://docs.nurama.com).
|
|
108
|
+
|
|
109
|
+
## Bot SDK
|
|
110
|
+
|
|
111
|
+
For programmatic / integration use cases, import `BotClient` from the `/bot` subpath. It authenticates with a long-lived API key (`nrm_bot_...`) instead of a JWT, defaults `baseURL` to the bot subdomain, and skips token refresh logic.
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
import BotClient from '@nurama/sdk/bot';
|
|
115
|
+
|
|
116
|
+
const bot = new BotClient(process.env.NURAMA_BOT_API_KEY);
|
|
117
|
+
const workspaces = await bot.workspace.listWorkspaces();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Constructor
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
new BotClient(apiKey: string, options?: {
|
|
124
|
+
baseURL?: string; // Default: 'https://bot.nurama.com'
|
|
125
|
+
websocketURL?: string; // Default: 'https://bot-ws.nurama.com'
|
|
126
|
+
fetch?: typeof fetch; // Custom fetch implementation
|
|
127
|
+
debug?: boolean;
|
|
128
|
+
enableCache?: boolean; // Default: true
|
|
129
|
+
cacheDurationSeconds?: number; // Default: 5
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Exposed Namespaces
|
|
134
|
+
|
|
135
|
+
`workspace`, `project`, `asset`, `chat`, `folder`, `invite`, `membership`, `notification`, `storage`, `task`, `tag`, `settings`, `shortlink`, `convo`, `board`, `version`, `config`, `product`, `public`, `socket`.
|
|
136
|
+
|
|
137
|
+
### WebSocket Subscriptions
|
|
138
|
+
|
|
139
|
+
Bots can subscribe to real-time events using the same `socket` API as the regular SDK. Connections go to the bot WS subdomain (`bot-ws.nurama.com` by default) and authenticate with the API key as the query param `token`.
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import BotClient from '@nurama/sdk/bot';
|
|
143
|
+
|
|
144
|
+
const bot = new BotClient(process.env.NURAMA_BOT_API_KEY!);
|
|
145
|
+
|
|
146
|
+
await bot.socket.subscribe(`/workspace/${workspaceId}`, 'notification', (event) => {
|
|
147
|
+
console.log('Bot received:', event.type, event);
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
API keys don't expire client-side, so the SDK skips the JWT-refresh path. If the server emits `tokenEvent: TokenExpired` or `tokenBlacklisted` for a bot connection (e.g., the key was revoked), the SDK disconnects without retrying.
|
|
152
|
+
|
|
153
|
+
The following namespaces are intentionally omitted (the server rejects bot calls to them with `botAccountRestricted`):
|
|
154
|
+
|
|
155
|
+
- `auth` — login, MFA, password, OAuth
|
|
156
|
+
- `user` — profile editing
|
|
157
|
+
- `payment`, `subscription` — billing
|
|
158
|
+
- `device` — push notification routing
|
|
159
|
+
|
|
160
|
+
Bot management (creating / rotating / deleting other bots) is also restricted server-side and is not exposed here. Workspace admins manage bots via the regular `NuramaClient` with their JWT.
|
|
161
|
+
|
|
162
|
+
### Browser Usage
|
|
163
|
+
|
|
164
|
+
```html
|
|
165
|
+
<script src="https://cdn.nurama.com/nurama-bot-sdk.min.js"></script>
|
|
166
|
+
<script>
|
|
167
|
+
const bot = new NuramaBotClient('nrm_bot_...');
|
|
168
|
+
bot.workspace.listWorkspaces().then(console.log);
|
|
169
|
+
</script>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Distribution Formats
|
|
173
|
+
|
|
174
|
+
The SDK is distributed in multiple formats:
|
|
175
|
+
|
|
176
|
+
- **ES Module**: `dist/NuramaClient.js` - For modern JavaScript environments with module support
|
|
177
|
+
- **Bot ES Module**: `dist/BotClient.js` — API-key client for bots, imported via `@nurama/sdk/bot`
|
|
178
|
+
- **Browser bundle**: `dist/browser/nurama-sdk.js` - IIFE bundle for direct browser usage
|
|
179
|
+
- **Bot Browser bundle**: `dist/browser/nurama-bot-sdk.js` — IIFE bot bundle (exposes `window.NuramaBotClient`)
|
|
180
|
+
- **TypeScript definitions**: `dist/NuramaClient.d.ts` - Type definitions for TypeScript projects
|
|
181
|
+
|
|
182
|
+
## Initiation Options
|
|
183
|
+
|
|
184
|
+
The `NuramaClient` constructor accepts various options for customization:
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
// Import the client
|
|
188
|
+
import NuramaClient from 'nurama-sdk';
|
|
189
|
+
|
|
190
|
+
// Initialize with options
|
|
191
|
+
const nurama = new NuramaClient('https://api.nurama.com', {
|
|
192
|
+
// Custom fetch implementation (optional)
|
|
193
|
+
fetch: customFetchFunction,
|
|
194
|
+
|
|
195
|
+
// Storage key names (optional)
|
|
196
|
+
tokenStorageKey: 'custom_access_token',
|
|
197
|
+
refreshTokenStorageKey: 'custom_refresh_token',
|
|
198
|
+
|
|
199
|
+
// Whether to run in browser mode (uses localStorage for token storage)
|
|
200
|
+
browserMode: false, // Default: false - set to true for browser environments
|
|
201
|
+
|
|
202
|
+
// WebSocket server URL (if different from API URL)
|
|
203
|
+
websocketURL: 'https://ws.nurama.com', // Socket.IO handles protocol conversion
|
|
204
|
+
|
|
205
|
+
// Token refresh settings
|
|
206
|
+
tokenExpiryBufferSeconds: 60,
|
|
207
|
+
tokenRefreshRetryDelayMs: 100,
|
|
208
|
+
tokenRefreshMaxWaitMs: 1000,
|
|
209
|
+
refreshLockTimeoutMs: 1000,
|
|
210
|
+
|
|
211
|
+
// Response caching settings
|
|
212
|
+
enableCache: true, // Enable/disable caching (default: true)
|
|
213
|
+
cacheDurationSeconds: 5, // Cache duration in seconds (default: 5)
|
|
214
|
+
|
|
215
|
+
// Enable debug mode
|
|
216
|
+
debug: false
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Response Caching
|
|
221
|
+
|
|
222
|
+
The Nurama SDK includes a built-in response caching mechanism that can improve performance by avoiding redundant API calls.
|
|
223
|
+
|
|
224
|
+
### Configuration
|
|
225
|
+
|
|
226
|
+
The caching system is configured through constructor options:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
const client = new NuramaClient('https://api.nurama.com', {
|
|
230
|
+
enableCache: true, // Enable/disable caching (default: true)
|
|
231
|
+
cacheDurationSeconds: 5, // Cache duration in seconds (default: 5)
|
|
232
|
+
debug: true // Enable debug logging to see cache hits
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Default Behavior
|
|
237
|
+
|
|
238
|
+
- **Enabled by default**: Caching is enabled unless explicitly disabled
|
|
239
|
+
- **5-second cache**: Responses are cached for 5 seconds by default
|
|
240
|
+
- **GET/HEAD and select POST requests**: GET and HEAD requests are cached, plus specific read-only POST endpoints
|
|
241
|
+
- **Notification endpoints**: POST requests to notification endpoints (`/v1/notifications/count`, `/v1/notifications/count/bulk`, etc.) are cached since they are read-only query operations
|
|
242
|
+
- **Concurrent request handling**: Multiple identical requests will share the same response
|
|
243
|
+
|
|
244
|
+
### Usage Examples
|
|
245
|
+
|
|
246
|
+
#### Basic Usage
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
// First call - makes API request and caches response
|
|
250
|
+
const users1 = await client.user.getUserSelf();
|
|
251
|
+
|
|
252
|
+
// Second call within 5 seconds - returns cached response
|
|
253
|
+
const users2 = await client.user.getUserSelf(); // Cache hit!
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### Bypassing Cache
|
|
257
|
+
|
|
258
|
+
Some route methods may support a `bypassCache` option:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
// This would bypass cache if the method supports it
|
|
262
|
+
const freshData = await client.user.getUserSelf({ bypassCache: true });
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### Concurrent Requests
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
// Both requests will share the same API call and response
|
|
269
|
+
const [users1, users2] = await Promise.all([
|
|
270
|
+
client.user.getUserSelf(),
|
|
271
|
+
client.user.getUserSelf()
|
|
272
|
+
]);
|
|
273
|
+
// Only one actual API request is made
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
#### Cache Management
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// Clear all cached responses
|
|
280
|
+
client.clearCache();
|
|
281
|
+
|
|
282
|
+
// Get cache statistics for debugging
|
|
283
|
+
const stats = client.getCacheStats();
|
|
284
|
+
console.log(`Cache size: ${stats.size}`);
|
|
285
|
+
console.log('Cache entries:', stats.entries);
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### Chat Reactions
|
|
289
|
+
|
|
290
|
+
The SDK supports creating and removing reactions on chat messages:
|
|
291
|
+
|
|
292
|
+
```javascript
|
|
293
|
+
// Add a reaction to a message
|
|
294
|
+
await client.chat.createReaction('messageId123', {
|
|
295
|
+
emoji: '👍'
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// Update an existing reaction (replaces user's previous reaction)
|
|
299
|
+
await client.chat.createReaction('messageId123', {
|
|
300
|
+
emoji: '❤️'
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// Remove your reaction from a message
|
|
304
|
+
await client.chat.removeReaction('messageId123');
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Upload Progress Persistence
|
|
308
|
+
|
|
309
|
+
The SDK now includes built-in upload progress persistence and cross-tab synchronization for multipart uploads. This feature allows uploads to continue tracking their progress even after page refreshes or when switching between browser tabs.
|
|
310
|
+
|
|
311
|
+
#### Features
|
|
312
|
+
|
|
313
|
+
- **Automatic Progress Persistence**: Upload progress is saved to localStorage during upload
|
|
314
|
+
- **Project Isolation**: Each project maintains separate upload sessions
|
|
315
|
+
- **Cross-Tab Synchronization**: Upload progress syncs across all browser tabs in real-time
|
|
316
|
+
- **Session Recovery**: Retrieve and resume tracking uploads after page refresh
|
|
317
|
+
- **Automatic Cleanup**: Old and completed sessions are cleaned up automatically
|
|
318
|
+
|
|
319
|
+
#### Usage
|
|
320
|
+
|
|
321
|
+
Enable progress persistence when uploading files:
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
// Upload with progress persistence enabled
|
|
325
|
+
const uploadResult = await client.asset.multipartUpload(
|
|
326
|
+
file,
|
|
327
|
+
signedUrls,
|
|
328
|
+
key,
|
|
329
|
+
uploadId,
|
|
330
|
+
{
|
|
331
|
+
// Enable automatic progress persistence
|
|
332
|
+
enableProgressPersistence: true,
|
|
333
|
+
|
|
334
|
+
// Required: Project ID for session isolation
|
|
335
|
+
projectId: 'project123',
|
|
336
|
+
|
|
337
|
+
// Optional: Custom session ID (auto-generated if not provided)
|
|
338
|
+
sessionId: 'custom-session-id',
|
|
339
|
+
|
|
340
|
+
// Optional: Additional metadata
|
|
341
|
+
fileName: 'video.mp4',
|
|
342
|
+
fileSize: 50 * 1024 * 1024, // 50MB
|
|
343
|
+
assetId: 'asset123', // Once available from backend
|
|
344
|
+
|
|
345
|
+
// Your existing progress callback still works
|
|
346
|
+
onProgress: (progress) => {
|
|
347
|
+
console.log(`Upload progress: ${progress.totalPercentComplete}%`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Managing Upload Sessions
|
|
354
|
+
|
|
355
|
+
Retrieve active upload sessions for a project:
|
|
356
|
+
|
|
357
|
+
```javascript
|
|
358
|
+
// Get all upload sessions for a project
|
|
359
|
+
const sessions = client.asset.getUploadSessions('project123');
|
|
360
|
+
sessions.forEach(session => {
|
|
361
|
+
console.log(`Upload ${session.fileName}: ${session.progress}% complete`);
|
|
362
|
+
console.log(`Status: ${session.status}`); // 'uploading', 'completing', 'completed', 'failed', 'cancelled'
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Get specific upload session
|
|
366
|
+
const session = client.asset.getUploadSession('project123', 'session-id');
|
|
367
|
+
|
|
368
|
+
// Remove a session manually
|
|
369
|
+
client.asset.removeUploadSession('project123', 'session-id');
|
|
370
|
+
|
|
371
|
+
// Clean up old sessions (default: 24 hours)
|
|
372
|
+
client.asset.cleanupUploadSessions('project123');
|
|
373
|
+
// Or specify custom age threshold
|
|
374
|
+
client.asset.cleanupUploadSessions('project123', 7 * 24 * 60 * 60 * 1000); // 7 days
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
#### Cross-Tab Synchronization
|
|
378
|
+
|
|
379
|
+
Listen for upload progress updates from other tabs:
|
|
380
|
+
|
|
381
|
+
```javascript
|
|
382
|
+
// Register a listener for cross-tab messages
|
|
383
|
+
client.asset.onUploadSessionMessage('my-listener', (message) => {
|
|
384
|
+
console.log(`Upload event from another tab:`, message);
|
|
385
|
+
|
|
386
|
+
switch (message.type) {
|
|
387
|
+
case 'upload_progress_update':
|
|
388
|
+
console.log(`Progress update for ${message.sessionId}: ${message.data.progress}%`);
|
|
389
|
+
break;
|
|
390
|
+
case 'upload_status_change':
|
|
391
|
+
console.log(`Status change for ${message.sessionId}: ${message.data.status}`);
|
|
392
|
+
break;
|
|
393
|
+
case 'upload_cleanup':
|
|
394
|
+
console.log(`Upload ${message.sessionId} was removed`);
|
|
395
|
+
break;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Unregister listener when done
|
|
400
|
+
client.asset.offUploadSessionMessage('my-listener');
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
#### Upload Session Data Structure
|
|
404
|
+
|
|
405
|
+
Each upload session contains the following information:
|
|
406
|
+
|
|
407
|
+
```typescript
|
|
408
|
+
interface UploadSessionData {
|
|
409
|
+
sessionId: string; // Unique session identifier
|
|
410
|
+
projectId: string; // Project ID for isolation
|
|
411
|
+
uploadId: string; // S3 multipart upload ID
|
|
412
|
+
key: string; // S3 object key
|
|
413
|
+
assetId?: string; // Backend asset ID (once available)
|
|
414
|
+
|
|
415
|
+
// Progress tracking
|
|
416
|
+
progress: number; // Overall progress (0-100)
|
|
417
|
+
partNumber: number; // Current part being uploaded
|
|
418
|
+
totalParts: number; // Total number of parts
|
|
419
|
+
completedParts: number[]; // Array of completed part numbers
|
|
420
|
+
|
|
421
|
+
// Metadata
|
|
422
|
+
fileName: string; // Original file name
|
|
423
|
+
fileSize: number; // File size in bytes
|
|
424
|
+
timestamp: number; // Last update timestamp
|
|
425
|
+
status: 'uploading' | 'completing' | 'completed' | 'failed' | 'cancelled';
|
|
426
|
+
|
|
427
|
+
// Optional error information
|
|
428
|
+
error?: string; // Error message if failed
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
#### Complete Example
|
|
433
|
+
|
|
434
|
+
```javascript
|
|
435
|
+
// Start an upload with persistence
|
|
436
|
+
const file = document.getElementById('fileInput').files[0];
|
|
437
|
+
const { signedUrlData } = await client.project.createAssets('project123', {
|
|
438
|
+
files: [{ name: file.name, sizeInMB: file.size / 1024 / 1024, checksum: 'abc123' }]
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
// Upload with progress persistence
|
|
442
|
+
await client.asset.multipartUpload(
|
|
443
|
+
file,
|
|
444
|
+
signedUrlData.urls,
|
|
445
|
+
signedUrlData.key,
|
|
446
|
+
signedUrlData.uploadId,
|
|
447
|
+
{
|
|
448
|
+
enableProgressPersistence: true,
|
|
449
|
+
projectId: 'project123',
|
|
450
|
+
fileName: file.name,
|
|
451
|
+
fileSize: file.size,
|
|
452
|
+
onProgress: updateProgressBar
|
|
453
|
+
}
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
// Complete the upload
|
|
457
|
+
await client.asset.completeUpload({
|
|
458
|
+
key: signedUrlData.key,
|
|
459
|
+
uploadId: signedUrlData.uploadId,
|
|
460
|
+
parts: uploadResult.parts
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
// Mark session as completed
|
|
464
|
+
client.asset.completeUploadSession('project123', sessionId);
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### File System Operations
|
|
468
|
+
|
|
469
|
+
The SDK now supports advanced file system operations for organizing and managing project assets and folders:
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
// Create a folder with file system integration
|
|
473
|
+
await client.project.createFolder('projectId123', 'creator', {
|
|
474
|
+
name: 'New Folder',
|
|
475
|
+
basePath: '/project/projectId123/creator'
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// Get items at a specific file system path
|
|
479
|
+
const items = await client.project.getItemsAtPath(
|
|
480
|
+
'projectId123',
|
|
481
|
+
'creator',
|
|
482
|
+
'/project/projectId123/creator/folder1',
|
|
483
|
+
{
|
|
484
|
+
limit: 20,
|
|
485
|
+
mediaTypes: ['image', 'video'],
|
|
486
|
+
nameSearch: 'test'
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
// Move items to a new location
|
|
491
|
+
await client.project.moveItemsToPath('projectId123', 'creator', {
|
|
492
|
+
itemPaths: ['/project/projectId123/creator/folder1/asset1'],
|
|
493
|
+
destinationPath: '/project/projectId123/creator/folder2'
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
// Publish items with file system support
|
|
497
|
+
await client.project.publishItems('projectId123', {
|
|
498
|
+
resourceIds: ['assetId1', 'folderId1'],
|
|
499
|
+
basePath: '/project/projectId123/creator',
|
|
500
|
+
sendEmailNotification: true
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Copy items to a new location
|
|
504
|
+
await client.project.copyItemsToPath('projectId123', 'creator', {
|
|
505
|
+
itemPaths: ['/project/projectId123/creator/folder1/asset1'],
|
|
506
|
+
destinationPath: '/project/projectId123/creator/folder2'
|
|
507
|
+
});
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Tag Management
|
|
511
|
+
|
|
512
|
+
The SDK supports tagging and untagging assets and folders:
|
|
513
|
+
|
|
514
|
+
```javascript
|
|
515
|
+
// Tag an asset
|
|
516
|
+
await client.asset.tagAsset('assetId123', {
|
|
517
|
+
tagId: 'tagId123'
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// Remove a tag from an asset
|
|
521
|
+
await client.asset.untagAsset('assetId123', {
|
|
522
|
+
tagId: 'tagId123'
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Tag a folder
|
|
526
|
+
await client.folder.tagFolder('folderId123', {
|
|
527
|
+
tagId: 'tagId123'
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
// Remove a tag from a folder
|
|
531
|
+
await client.folder.untagFolder('folderId123', {
|
|
532
|
+
tagId: 'tagId123'
|
|
533
|
+
});
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Debug Output
|
|
537
|
+
|
|
538
|
+
When `debug: true` is enabled, you'll see cache-related log messages:
|
|
539
|
+
|
|
540
|
+
```
|
|
541
|
+
[DEBUG] Cache hit for key: {"endpoint":"/v1/users","method":"GET","params":{},"body":null}
|
|
542
|
+
[DEBUG] Cached response for key: {"endpoint":"/v1/users","method":"GET","params":{},"body":null}
|
|
543
|
+
[DEBUG] Returning pending promise for concurrent request: {"endpoint":"/v1/users","method":"GET","params":{},"body":null}
|
|
544
|
+
[DEBUG] Cache cleared
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
### Cache Key Generation
|
|
548
|
+
|
|
549
|
+
Cache keys are generated based on:
|
|
550
|
+
- Endpoint URL
|
|
551
|
+
- HTTP method
|
|
552
|
+
- Query parameters (sorted)
|
|
553
|
+
- Request body
|
|
554
|
+
|
|
555
|
+
This ensures that different requests are cached separately while identical requests share the same cache entry.
|
|
556
|
+
|
|
557
|
+
### Limitations
|
|
558
|
+
|
|
559
|
+
- Only GET and HEAD requests are cached
|
|
560
|
+
- Cache is in-memory only (cleared when client is destroyed)
|
|
561
|
+
- No persistent storage across page reloads in browser mode
|
|
562
|
+
- Authentication/refresh token requests are never cached
|
|
563
|
+
|
|
564
|
+
## URL Parameter Handling
|
|
565
|
+
|
|
566
|
+
The Nurama SDK includes a sophisticated URL parameter utility that automatically transforms complex JavaScript objects into URL-safe query parameters for all API requests.
|
|
567
|
+
|
|
568
|
+
### Automatic Integration
|
|
569
|
+
|
|
570
|
+
The URL parameter handling is **automatically applied to ALL routes** through the `NuramaClient._request()` method. Every route that accepts parameters benefits from this functionality without requiring individual updates. You can pass complex parameter objects containing arrays, nested objects, and various data types, and they will be properly serialized:
|
|
571
|
+
|
|
572
|
+
```javascript
|
|
573
|
+
// Complex parameters are automatically handled
|
|
574
|
+
const assets = await client.project.getAssets('project123', 'creator', {
|
|
575
|
+
page: 1,
|
|
576
|
+
limit: 20,
|
|
577
|
+
search: 'test query',
|
|
578
|
+
tags: ['important', 'urgent'], // Arrays become comma-separated
|
|
579
|
+
filters: { // Objects become JSON strings
|
|
580
|
+
status: ['active', 'pending'],
|
|
581
|
+
dateRange: {
|
|
582
|
+
start: '2024-01-01',
|
|
583
|
+
end: '2024-12-31'
|
|
584
|
+
}
|
|
585
|
+
},
|
|
586
|
+
sort: { createdAt: -1, priority: 1 },
|
|
587
|
+
active: true, // Booleans converted to strings
|
|
588
|
+
startDate: new Date('2024-01-01') // Dates properly formatted
|
|
589
|
+
});
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
### Default Configuration
|
|
593
|
+
|
|
594
|
+
The SDK uses optimized defaults for the Nurama API:
|
|
595
|
+
|
|
596
|
+
- **arrayFormat**: `'comma'` - Arrays are serialized as comma-separated values
|
|
597
|
+
- **objectFormat**: `'bracket'` - Objects are serialized using bracket notation
|
|
598
|
+
- **encode**: `true` - All values are URL encoded
|
|
599
|
+
|
|
600
|
+
### Route Coverage
|
|
601
|
+
|
|
602
|
+
**All routes automatically inherit this functionality**, including:
|
|
603
|
+
|
|
604
|
+
- ✅ **Authentication routes** (`client.auth.*`)
|
|
605
|
+
- ✅ **Asset routes** (`client.asset.*`)
|
|
606
|
+
- ✅ **Chat routes** (`client.chat.*`)
|
|
607
|
+
- ✅ **Folder routes** (`client.folder.*`)
|
|
608
|
+
- ✅ **Invite routes** (`client.invite.*`)
|
|
609
|
+
- ✅ **Membership routes** (`client.membership.*`)
|
|
610
|
+
- ✅ **Notification routes** (`client.notification.*`)
|
|
611
|
+
- ✅ **Package routes** (`client.package.*`)
|
|
612
|
+
- ✅ **Project routes** (`client.project.*`)
|
|
613
|
+
- ✅ **Storage routes** (`client.storage.*`)
|
|
614
|
+
- ✅ **Subscription routes** (`client.subscription.*`)
|
|
615
|
+
- ✅ **Task routes** (`client.task.*`)
|
|
616
|
+
- ✅ **User routes** (`client.user.*`)
|
|
617
|
+
- ✅ **Version routes** (`client.version.*`)
|
|
618
|
+
- ✅ **Workspace routes** (`client.workspace.*`)
|
|
619
|
+
|
|
620
|
+
### Supported Data Types
|
|
621
|
+
|
|
622
|
+
| Type | Behavior | Example Input | Example Output |
|
|
623
|
+
|------|----------|---------------|----------------|
|
|
624
|
+
| **String** | URL encoded | `"John Doe"` | `"John%20Doe"` |
|
|
625
|
+
| **Number** | Converted to string | `25` | `"25"` |
|
|
626
|
+
| **Boolean** | Converted to string | `true` | `"true"` |
|
|
627
|
+
| **Array** | Comma-separated, encoded | `["a", "b", "c"]` | `"a%2Cb%2Cc"` |
|
|
628
|
+
| **Object** | Dot notation, encoded | `{x: 1}` | `"obj.x=1"` |
|
|
629
|
+
| **Date** | toString(), encoded | `new Date()` | `"Mon%20Jan%2001..."` |
|
|
630
|
+
| **null/undefined** | Filtered out | `null` | (not included) |
|
|
631
|
+
|
|
632
|
+
### Real-World Examples
|
|
633
|
+
|
|
634
|
+
#### Asset Search with Complex Parameters
|
|
635
|
+
```javascript
|
|
636
|
+
await client.project.getAssets('project123', 'creator', {
|
|
637
|
+
mediaTypes: ['image', 'video'], // → "image%2Cvideo"
|
|
638
|
+
includeChats: true, // → "true"
|
|
639
|
+
folderId: 'folder123', // → "folder123"
|
|
640
|
+
sort: { name: 1, createdAt: -1 }, // → "sort[name]=1&sort[createdAt]=-1"
|
|
641
|
+
search: 'test query' // → "test%20query"
|
|
642
|
+
});
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
#### Chat Parameters
|
|
646
|
+
```javascript
|
|
647
|
+
await client.chat.getMessages('chat123', {
|
|
648
|
+
limit: 50, // → "50"
|
|
649
|
+
sort: { createdAt: -1 }, // → "sort[createdAt]=-1"
|
|
650
|
+
replies: 10, // → "10"
|
|
651
|
+
mentions: ['user1', 'user2'] // → "user1%2Cuser2"
|
|
652
|
+
});
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
#### Notification Queries
|
|
656
|
+
```javascript
|
|
657
|
+
await client.notification.getNotifications({
|
|
658
|
+
channels: ['project:123', 'workspace:456'], // → "project%3A123%2Cworkspace%3A456"
|
|
659
|
+
types: ['comment', 'mention'], // → "comment%2Cmention"
|
|
660
|
+
paginate: 'cursor', // → "cursor"
|
|
661
|
+
limit: 20, // → "20"
|
|
662
|
+
createdAfter: Date.now() - 86400000 // → "1704067200000"
|
|
663
|
+
});
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
### Manual Usage
|
|
667
|
+
|
|
668
|
+
The URL parameter utility can also be used independently:
|
|
669
|
+
|
|
670
|
+
```javascript
|
|
671
|
+
import { parseUrlParams, paramsToUrlSearchParams } from 'nurama-sdk/utils/urlParams';
|
|
672
|
+
|
|
673
|
+
// Convert parameters to string record
|
|
674
|
+
const stringParams = parseUrlParams({
|
|
675
|
+
tags: ['a', 'b', 'c'],
|
|
676
|
+
filter: { status: 'active' }
|
|
677
|
+
});
|
|
678
|
+
// Result: { tags: 'a%2Cb%2Cc', filter: '%7B%22status%22%3A%22active%22%7D' }
|
|
679
|
+
|
|
680
|
+
// Create URLSearchParams object
|
|
681
|
+
const urlParams = paramsToUrlSearchParams({
|
|
682
|
+
page: 1,
|
|
683
|
+
limit: 10,
|
|
684
|
+
active: true
|
|
685
|
+
});
|
|
686
|
+
console.log(urlParams.toString()); // "page=1&limit=10&active=true"
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Configuration Options
|
|
690
|
+
|
|
691
|
+
The utility supports configuration options (though the defaults work well for the Nurama API):
|
|
692
|
+
|
|
693
|
+
```javascript
|
|
694
|
+
import { parseUrlParams } from 'nurama-sdk/utils/urlParams';
|
|
695
|
+
|
|
696
|
+
const result = parseUrlParams(params, {
|
|
697
|
+
arrayFormat: 'comma', // 'comma' or 'brackets' (key[]=val1&key[]=val2)
|
|
698
|
+
objectFormat: 'bracket', // 'bracket', 'dot', or 'json' (JSON stringified)
|
|
699
|
+
encode: true // Whether to URL encode values (default: true)
|
|
700
|
+
});
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
### Technical Implementation
|
|
704
|
+
|
|
705
|
+
- **Minimal overhead**: Parameter processing only occurs when parameters are provided
|
|
706
|
+
- **Efficient encoding**: Uses native `encodeURIComponent()`
|
|
707
|
+
- **Caching friendly**: Encoded parameters work properly with response caching
|
|
708
|
+
- **Proper encoding**: Manual query string building to avoid double-encoding issues
|
|
709
|
+
- **Comprehensive test coverage**: 36 total tests (26 unit + 10 integration)
|
|
710
|
+
|
|
711
|
+
### Migration Notes
|
|
712
|
+
|
|
713
|
+
The implementation provides backward compatibility with all existing route method signatures remaining unchanged. Only the URL encoding behavior has been updated to be more robust and consistent with `encode: true` by default.
|
|
714
|
+
|
|
715
|
+
## API Client Methods
|
|
716
|
+
|
|
717
|
+
This section lists the methods available under `client.<module>`. Full API documentation can be found in the Nurama backend repo or its Swagger/OpenAPI definition.
|
|
718
|
+
|
|
719
|
+
### Authentication (`client.auth`)
|
|
720
|
+
|
|
721
|
+
* `async register(userData)`: Registers a new user (`firstName`, `lastName`, `email`, `password`).
|
|
722
|
+
* `async login(credentials)`: Logs in a user (`login`, `password`). Handles MFA challenges.
|
|
723
|
+
* `async logout(refreshToken?)`: Clears stored tokens and invalidates the refresh token.
|
|
724
|
+
* `async refreshTokens(refreshToken?)`: Exchanges a refresh token for new access and refresh tokens.
|
|
725
|
+
* `async forgotPassword(email)`: Sends a password reset email.
|
|
726
|
+
* `async resetPassword({ token, newPassword })`: Resets the password using a token.
|
|
727
|
+
* `async sendVerificationEmail()`: Sends an email verification link to the logged-in user.
|
|
728
|
+
* `async verifyEmail(token)`: Verifies the user's email using a token.
|
|
729
|
+
* `async enableMfa()`: Enables multi-factor authentication, returns setup details.
|
|
730
|
+
* `async verifyMfa(mfaToken)`: Verifies an MFA token (during setup or login).
|
|
731
|
+
* `async disableMfa(mfaToken)`: Disables MFA using a current MFA token.
|
|
732
|
+
* `tokenValid()`: Checks if the current JWT token exists and is valid. Returns `true` if the token exists and is not expired, `false` otherwise.
|
|
733
|
+
|
|
734
|
+
### Cache Management (`client`)
|
|
735
|
+
|
|
736
|
+
* `clearCache()`: Clears all cached responses.
|
|
737
|
+
* `getCacheStats()`: Returns cache statistics including size and entry details for debugging.
|
|
738
|
+
|
|
739
|
+
### User (`client.user`)
|
|
740
|
+
|
|
741
|
+
* `async getUserSelf()`: Retrieves the full profile of logged in user.
|
|
742
|
+
* `async getUser(userId)`: Retrieves the public profile of a specific user.
|
|
743
|
+
* `async updateSelf(updateData)`: Updates the profile of the currently logged-in user (`email`, `userName`, `password`, `firstName`, `middleName`, `lastName`, `company`, `displayName`).
|
|
744
|
+
* `async deleteCurrentUser()`: Marks the logged-in user's account for deletion.
|
|
745
|
+
* `async createAvatar(fileData)`: Creates a new avatar asset for the user, returns signed URL data.
|
|
746
|
+
* `async updateAvatar(fileData)`: Updates the user's avatar asset, returns signed URL data.
|
|
747
|
+
* `async updatePreferences(preferenceData)`: Updates user preferences (e.g., `hide` array).
|
|
748
|
+
|
|
749
|
+
### Workspace (`client.workspace`)
|
|
750
|
+
|
|
751
|
+
* `async createWorkspace(data)`: Creates a new workspace (`name`, `description?`).
|
|
752
|
+
* `async getWorkspace(workspaceId)`: Gets workspace details by ID.
|
|
753
|
+
* `async listWorkspaces(sortParams?)`: Gets all workspaces for the current user, with optional sorting.
|
|
754
|
+
* `async updateWorkspace(workspaceId, updateData)`: Updates workspace details (`name?`, `description?`, `updateSlug?`).
|
|
755
|
+
* `async deleteWorkspace(workspaceId)`: Marks a workspace for deletion.
|
|
756
|
+
* `async createLogo(workspaceId, fileData)`: Creates a logo asset, returns signed URL data.
|
|
757
|
+
* `async updateLogo(workspaceId, fileData)`: Updates the logo asset, returns signed URL data.
|
|
758
|
+
* `async createIcon(workspaceId, fileData)`: Creates an icon asset, returns signed URL data.
|
|
759
|
+
* `async updateIcon(workspaceId, fileData)`: Updates the icon asset, returns signed URL data.
|
|
760
|
+
* `async listProjects(workspaceId)`: Retrieves projects within a workspace.
|
|
761
|
+
* `async updateSetting(workspaceId, settingName, value)`: Updates a specific boolean workspace setting.
|
|
762
|
+
|
|
763
|
+
### Project (`client.project`)
|
|
764
|
+
|
|
765
|
+
* `async createProject(projectData)`: Creates a new project (`name`, `workspaceId`).
|
|
766
|
+
* `async getProjects()`: Retrieves projects accessible by the user (no pagination).
|
|
767
|
+
* `async getProject(projectId)`: Retrieves a specific project by ID.
|
|
768
|
+
* `async updateProject(projectId, updateData)`: Updates project details (`name?`, `description?`, `updateSlug?`).
|
|
769
|
+
* `async deleteProject(projectId)`: Marks a project for deletion.
|
|
770
|
+
* `async createLogo(projectId, logoData)`: Creates a logo asset for the project, returns signed URL data.
|
|
771
|
+
* `async updateLogo(projectId, logoData)`: Updates the project logo asset, returns signed URL data.
|
|
772
|
+
* `async createAssets(projectId, fileUploadBody)`: Creates multiple assets from file data, returns signed URL data for each.
|
|
773
|
+
* `async getAssets(projectId, visibility, params?)`: Gets assets for one visibility tier (`'creator'` or `'reviewer'`) with pagination, sorting and filtering.
|
|
774
|
+
* `async getHomeFeed(projectId, visibility, params?)`: Gets the feed (assets with inline chat data) for one visibility tier.
|
|
775
|
+
* `async getHomeFeed(projectId, visibility, params?)`: Gets feed based on visibility.
|
|
776
|
+
* `async getFolders(projectId, visibility, params?)`: Gets folders for one visibility tier.
|
|
777
|
+
* `async getFolders(projectId, visibility, params?)`: Gets folders based on visibility.
|
|
778
|
+
* `async getProjectChat(projectId, visibility, params?)`: Gets the project chat for one visibility tier.
|
|
779
|
+
* `async getProjectChat(projectId, visibility, params?)`: Gets chat based on visibility.
|
|
780
|
+
* `async createSubmission(projectId, submissionData)`: Creates a new submission.
|
|
781
|
+
* `async getSubmissions(projectId, params?)`: Gets submissions for the project.
|
|
782
|
+
* `async getSubmission(projectId, submissionId)`: Gets a specific submission.
|
|
783
|
+
* `async getSubmissionAssets(projectId, submissionId, params?)`: Gets assets within a submission.
|
|
784
|
+
* `async publishAssets(projectId, assetData)`: Publishes a list of assets.
|
|
785
|
+
* `async unpublishAssets(projectId, assetData)`: Unpublishes a list of assets.
|
|
786
|
+
* `async createFolder(projectId, visibility, folderData)`: Creates a folder with file system integration (`name`, `basePath?`).
|
|
787
|
+
* `async publishItems(projectId, publishData)`: Publishes items (assets/folders) with file system support (`resourceIds`, `basePath?`, `sendEmailNotification?`).
|
|
788
|
+
* `async unpublishItems(projectId, unpublishData)`: Unpublishes items from file system (`resourceIds`).
|
|
789
|
+
* `async getItemsAtPath(projectId, visibility, path, params?)`: Gets items at a specific file system path with advanced filtering and pagination.
|
|
790
|
+
* `async moveItemsToPath(projectId, visibility, moveData)`: Moves items to a new file system path (`itemPaths`, `destinationPath`).
|
|
791
|
+
* `async copyItemsToPath(projectId, visibility, copyData)`: Copies items to a new file system path (`itemPaths`, `destinationPath`).
|
|
792
|
+
* `async deleteItemsAtPath(projectId, visibility, deleteData)`: Deletes items from file system path (`itemPaths`).
|
|
793
|
+
|
|
794
|
+
### Folder (`client.folder`)
|
|
795
|
+
|
|
796
|
+
* `async getFolder(folderId)`: Retrieves folder details.
|
|
797
|
+
* `async updateFolder(folderId, data)`: Updates the folder's name and/or color (`name?`, `color?`).
|
|
798
|
+
* `async updateFolderIcon(folderId, data)`: Updates the folder's icon (`name`, `checksum`, `sizeInMB`).
|
|
799
|
+
* `async deleteFolder(folderId)`: Deletes a folder.
|
|
800
|
+
* `async getFoldersAssets(folderId, params?)`: Gets assets within a folder (pagination, sorting).
|
|
801
|
+
* `async tagFolder(folderId, tagData)`: Tags a folder with a specific tag (`tagId`).
|
|
802
|
+
* `async untagFolder(folderId, untagData)`: Removes a tag from a folder (`tagId`).
|
|
803
|
+
* `async updateFolderName(folderId, data)`: **@deprecated** Use `updateFolder` instead. Updates the folder's name.
|
|
804
|
+
|
|
805
|
+
### Asset (`client.asset`)
|
|
806
|
+
|
|
807
|
+
* `async getAsset(assetId)`: Retrieves asset details.
|
|
808
|
+
* `async updateAsset(assetId, updateData)`: Updates asset details (`name?`, `meta?`, `tags?`, `folderId?`).
|
|
809
|
+
* `async deleteAsset(assetId)`: Marks an asset for deletion.
|
|
810
|
+
* `async getFile(assetId, fileId)`: Retrieves a specific file object within an asset.
|
|
811
|
+
* `async getFilesByFunctionType(assetId, functionType)`: Gets files by function type (e.g., 'thumbnail').
|
|
812
|
+
* `async completeUpload(uploadData)`: Completes a multipart upload initiated elsewhere.
|
|
813
|
+
* `async multipartUpload(file, signedUrls, key, uploadId, options?)`: Handles multipart file upload using pre-signed URLs with optional progress persistence.
|
|
814
|
+
* `async getAssetPage(assetId, params?)`: Gets the page number an asset appears on based on filter/sort.
|
|
815
|
+
* `async repairAssets(assetIds)`: Initiates repair process for assets.
|
|
816
|
+
* `async downloadAssets(assetIds)`: Initiates download process for assets.
|
|
817
|
+
* `async tagAsset(assetId, tagData)`: Tags an asset with a specific tag (`tagId`).
|
|
818
|
+
* `async untagAsset(assetId, untagData)`: Removes a tag from an asset (`tagId`).
|
|
819
|
+
|
|
820
|
+
**Upload Session Management Methods:**
|
|
821
|
+
* `getUploadSessions(projectId)`: Get all active upload sessions for a project.
|
|
822
|
+
* `getUploadSession(projectId, sessionId)`: Get a specific upload session.
|
|
823
|
+
* `removeUploadSession(projectId, sessionId)`: Remove an upload session.
|
|
824
|
+
* `cleanupUploadSessions(projectId, olderThanMs?)`: Clean up old upload sessions.
|
|
825
|
+
* `onUploadSessionMessage(listenerId, callback)`: Register a cross-tab message listener.
|
|
826
|
+
* `offUploadSessionMessage(listenerId)`: Unregister a cross-tab message listener.
|
|
827
|
+
* `completeUploadSession(projectId, sessionId)`: Mark an upload session as completed.
|
|
828
|
+
|
|
829
|
+
### Chat (`client.chat`)
|
|
830
|
+
|
|
831
|
+
* `async createTopicChat(data)`: Creates a chat linked to a topic (project or asset).
|
|
832
|
+
* `async getChatByTopicId(topicId, params)`: Gets chat details by its topic ID.
|
|
833
|
+
* `async createMemberChat(data)`: Creates a direct/group chat between members.
|
|
834
|
+
* `async getUsersMemberChats(params?)`: Gets member chats the user is part of.
|
|
835
|
+
* `async getMemberChat(chatId)`: Gets details of a specific member chat.
|
|
836
|
+
* `async updateMemberChat(chatId, data)`: Updates a member chat (subject, color, etc.).
|
|
837
|
+
* `async deleteMemberChat(chatId)`: Deletes a member chat.
|
|
838
|
+
* `async getAddableMembers(chatId)`: Gets users that can be added to a member chat.
|
|
839
|
+
* `async addMembers(chatId, data)`: Adds members (`memberIds`) to a chat.
|
|
840
|
+
* `async removeMembers(chatId, data)`: Removes members (`memberIds`) from a chat.
|
|
841
|
+
* `async getChat(chatId)`: Retrieves details for any chat type by ID.
|
|
842
|
+
* `async updateChatSubject(chatId, data)`: Updates the subject of any chat type.
|
|
843
|
+
* `async deleteChat(chatId)`: Deletes any chat type.
|
|
844
|
+
* `async createMessage(chatId, data)`: Creates a new message in a chat.
|
|
845
|
+
* `async createAssetChatAndMessage(assetId, visibility, data, params?)`: Creates a message in an asset's chat (potentially creating the chat).
|
|
846
|
+
* `async getMessages(chatId, params?)`: Gets messages in a chat (pagination, sorting).
|
|
847
|
+
* `async getMessage(messageId, params?)`: Gets a specific message by ID.
|
|
848
|
+
* `async reviseMessage(messageId, data)`: Updates the content/mentions of a message.
|
|
849
|
+
* `async deleteMessage(messageId)`: Deletes a message.
|
|
850
|
+
* `async getMessagePage(messageId, params?)`: Gets the page number a message appears on.
|
|
851
|
+
* `async getReplies(messageId, params?)`: Gets replies to a specific message.
|
|
852
|
+
* `async addAttachments(messageId, attachments)`: Adds file attachments to a message.
|
|
853
|
+
* `async removeAttachment(messageId, assetId)`: Removes an attachment from a message.
|
|
854
|
+
* `async getMentions(params?)`: Gets messages where the current user is mentioned.
|
|
855
|
+
* `async generateSummary(chatId, data)`: Generates an AI summary for a chat.
|
|
856
|
+
* `async getSummaries(chatId, params?)`: Retrieves previously generated summaries for a chat.
|
|
857
|
+
* `async createReaction(messageId, data)`: Creates or updates a reaction on a chat message (`emoji`).
|
|
858
|
+
* `async removeReaction(messageId)`: Removes the authenticated user's reaction from a chat message.
|
|
859
|
+
|
|
860
|
+
### Membership (`client.membership`)
|
|
861
|
+
|
|
862
|
+
* `async getMyMemberships()`: Retrieves the logged-in user's membership records.
|
|
863
|
+
* `async getWorkspaceMemberships(workspaceId, params?)`: Gets memberships for a workspace.
|
|
864
|
+
* `async getProjectMemberships(projectId, params?)`: Gets memberships for a project.
|
|
865
|
+
* `async deleteMembership(membershipId)`: Deletes a specific membership record.
|
|
866
|
+
* `async leaveResource(resourceId)`: Allows the user to leave a workspace or project.
|
|
867
|
+
* `async addRole(data)`: Adds a role to a membership (`membershipId`, `role`).
|
|
868
|
+
* `async removeRole(data)`: Removes a role from a membership (`membershipId`, `role`).
|
|
869
|
+
* `async getProjectMentionableUsers(projectId, visibility)`: Gets users mentionable in a project.
|
|
870
|
+
|
|
871
|
+
### Invite (`client.invite`)
|
|
872
|
+
|
|
873
|
+
* `async inviteUser(data)`: Invites a user (`inviteeEmail`) to a resource (`resourceType`, `resourceId`) with a specific `role`.
|
|
874
|
+
* `async getInvites(params?)`: Retrieves invites involving the current user (as inviter or invitee).
|
|
875
|
+
* `async getInviteById(inviteId)`: Gets details of a specific invite.
|
|
876
|
+
* `async cancelInvite(inviteId)`: Cancels an active invite.
|
|
877
|
+
* `async getInvitesForResource(resourceId, params?)`: Gets invites associated with a specific resource.
|
|
878
|
+
* `async acceptInvite(inviteId)`: Accepts an invite.
|
|
879
|
+
* `async resendInvite(inviteId)`: Resends an invite email.
|
|
880
|
+
|
|
881
|
+
### Notification (`client.notification`)
|
|
882
|
+
|
|
883
|
+
* `async getNotifications(data)`: Retrieves notifications for channels (pagination, filtering).
|
|
884
|
+
* `async getNewNotifications(data)`: Gets new notifications since last seen.
|
|
885
|
+
* `async getNewNotificationCount(data)`: Gets the count of new notifications for channels.
|
|
886
|
+
* `async getNewNotificationCountBulk(data)`: Gets counts for multiple channel queries.
|
|
887
|
+
* `async getUsersLastNotificationsSeen(data)`: Retrieves the last seen timestamp for channels.
|
|
888
|
+
* `async updateUsersLastSeen(data)`: Updates the last seen timestamp for channels.
|
|
889
|
+
|
|
890
|
+
### Subscription (`client.subscription`)
|
|
891
|
+
|
|
892
|
+
* `async getWorkspaceSubscriptions(workspaceId, params?)`: Retrieves subscriptions for a workspace.
|
|
893
|
+
* `async getMySubscriptions(params?)`: Retrieves subscriptions owned by the user.
|
|
894
|
+
* `async getResourceLimits(resourceId)`: Gets storage/seat limits for a resource (workspace).
|
|
895
|
+
* `async getSeatUsage(resourceId)`: Gets seat usage for a resource (workspace).
|
|
896
|
+
* `async getStorageUsage(resourceId)`: Gets storage usage for a resource (workspace).
|
|
897
|
+
|
|
898
|
+
### Package (`client.package`)
|
|
899
|
+
|
|
900
|
+
* `async getWorkspacePackages(workspaceId)`: Retrieves available subscription packages for a workspace.
|
|
901
|
+
|
|
902
|
+
### Task (`client.task`)
|
|
903
|
+
|
|
904
|
+
* `async getMyTasks(params?)`: Retrieves tasks assigned to the user (pagination, sorting, filtering).
|
|
905
|
+
* `async updateTaskStatus(taskId, data)`: Updates the status of a task.
|
|
906
|
+
* `async acknowledgeTask(taskId)`: Toggles the acknowledgement status of a task.
|
|
907
|
+
* `async getUnacknowledgedTaskCount(projectId)`: Gets the count of unacknowledged tasks in a project.
|
|
908
|
+
|
|
909
|
+
### Storage (`client.storage`)
|
|
910
|
+
|
|
911
|
+
* `async getStorageChart(resourceType, resourceId, params?)`: Retrieves storage usage chart data.
|
|
912
|
+
* `async getStorageRecord(resourceType, resourceId)`: Retrieves the latest storage record for a resource.
|
|
913
|
+
|
|
914
|
+
### Tag (`client.tag`)
|
|
915
|
+
|
|
916
|
+
* `async createTag(data)`: Creates a new tag (`name`, `ownerResourceType`, `ownerResourceId`, `color`).
|
|
917
|
+
* `async getTags(resourceType, resourceId, params?)`: Retrieves tags for a resource (pagination, sorting).
|
|
918
|
+
* `async updateTag(tagId, updateData)`: Updates tag details (`name?`, `color?`).
|
|
919
|
+
* `async deleteTag(tagId)`: Deletes a tag.
|
|
920
|
+
|
|
921
|
+
### Config (`client.config`)
|
|
922
|
+
|
|
923
|
+
* `async getConfig()`: Get platform configuration data (limits, colors, etc.).
|
|
924
|
+
|
|
925
|
+
### Version (`client.version`)
|
|
926
|
+
|
|
927
|
+
* `async getCommitHash()`: Retrieves the latest git commit hash of the deployed backend application.
|
|
928
|
+
* `getVersion()`: Returns SDK version information including version number, build timestamp, build hash, and git commit. This is a synchronous method that returns the version info embedded during the build process.
|
|
929
|
+
|
|
930
|
+
### WebSocket (`client.socket`)
|
|
931
|
+
|
|
932
|
+
* `connect(channel, options?)`: Connect to a WebSocket channel (e.g., `/user/123`, `/workspace/456`).
|
|
933
|
+
* `disconnect(channel)`: Disconnect from a specific channel.
|
|
934
|
+
* `disconnectAll()`: Disconnect from all connected channels.
|
|
935
|
+
* `subscribe(channel, event, callback)`: Subscribe to an event (e.g., 'notification') on a channel.
|
|
936
|
+
* `unsubscribe(channel, event, callback?)`: Unsubscribe from an event on a channel.
|
|
937
|
+
* `isConnected(channel)`: Check if currently connected to a specific channel.
|
|
938
|
+
|
|
939
|
+
## SDK Version Tracking
|
|
940
|
+
|
|
941
|
+
Releases follow [semantic versioning](https://semver.org/) and are published
|
|
942
|
+
automatically: every production deploy that changes the SDK publishes the next
|
|
943
|
+
patch version. Building the SDK never changes `package.json`.
|
|
944
|
+
|
|
945
|
+
`getVersion()` reports what the running bundle was built from:
|
|
946
|
+
|
|
947
|
+
- **Published releases** report their version, e.g. `1.4.3`.
|
|
948
|
+
- **Any other build** reports `<base version>+<source hash>`, e.g.
|
|
949
|
+
`1.4.0+60ddbec0`, so it is never mistaken for a release. The same source gives
|
|
950
|
+
the same string on any machine.
|
|
951
|
+
|
|
952
|
+
Alongside the version it carries `buildTimestamp`, `buildHash` (MD5 of the SDK
|
|
953
|
+
source), `gitCommit`, and `dirty` (the build included uncommitted SDK source
|
|
954
|
+
changes).
|
|
955
|
+
|
|
956
|
+
**Generated files:** `src/version.ts` is written by
|
|
957
|
+
`scripts/generate-version-info.cjs` before each build (git-ignored).
|
|
958
|
+
|
|
959
|
+
**Watch mode:** `pnpm dev` rebuilds `dist/` on every source change and keeps the
|
|
960
|
+
dev version current. Inside the Nurama monorepo it starts automatically with the
|
|
961
|
+
web app's dev server.
|
|
962
|
+
|
|
963
|
+
### Usage in Frontend Code
|
|
964
|
+
|
|
965
|
+
```typescript
|
|
966
|
+
import nuramaClient from '@/lib/nuramaSdk';
|
|
967
|
+
|
|
968
|
+
// Get SDK version information
|
|
969
|
+
const versionInfo = nuramaClient.getVersion();
|
|
970
|
+
|
|
971
|
+
console.log('SDK Version:', versionInfo.version);
|
|
972
|
+
console.log('Built:', new Date(versionInfo.buildTimestamp).toLocaleString());
|
|
973
|
+
console.log('Build Hash:', versionInfo.buildHash);
|
|
974
|
+
console.log('Git Commit:', versionInfo.gitCommit);
|
|
975
|
+
|
|
976
|
+
// Example output:
|
|
977
|
+
// SDK Version: 1.4.0
|
|
978
|
+
// Built: 10/11/2025, 2:01:01 AM
|
|
979
|
+
// Build Hash: 7db059000a1af183dbfe27dd73017015
|
|
980
|
+
// Git Commit: d13a1e0
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
**TypeScript Type:**
|
|
984
|
+
|
|
985
|
+
```typescript
|
|
986
|
+
import { type SDKVersionInfo } from 'nurama-sdk';
|
|
987
|
+
|
|
988
|
+
interface SDKVersionInfo {
|
|
989
|
+
version: string; // Semantic version (e.g., "1.3.3")
|
|
990
|
+
buildTimestamp: string; // ISO timestamp of build
|
|
991
|
+
buildHash: string; // MD5 hash of source files
|
|
992
|
+
gitCommit: string; // Short git commit hash
|
|
993
|
+
}
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
### Build Examples
|
|
997
|
+
|
|
998
|
+
**First Build (or after code changes):**
|
|
999
|
+
```bash
|
|
1000
|
+
$ npm run build
|
|
1001
|
+
|
|
1002
|
+
[Build Hash] Calculating source code hash...
|
|
1003
|
+
[Build Hash] Current hash: 7db059000a1af183dbfe27dd73017015
|
|
1004
|
+
[Build Hash] 🔄 Changes detected - version will be updated
|
|
1005
|
+
[Version Gen] Incrementing version: 1.3.2 → 1.3.3
|
|
1006
|
+
[Version Gen] ✅ Version info generated successfully
|
|
1007
|
+
...
|
|
1008
|
+
[Build Metadata] ✅ Build metadata saved successfully
|
|
1009
|
+
[Build Metadata] Version: 1.3.3
|
|
1010
|
+
```
|
|
1011
|
+
|
|
1012
|
+
**Rebuild Without Changes:**
|
|
1013
|
+
```bash
|
|
1014
|
+
$ npm run build
|
|
1015
|
+
|
|
1016
|
+
[Build Hash] Calculating source code hash...
|
|
1017
|
+
[Build Hash] Current hash: 7db059000a1af183dbfe27dd73017015
|
|
1018
|
+
[Build Hash] ✅ No changes detected - using existing version
|
|
1019
|
+
[Build Hash] Version: 1.3.3
|
|
1020
|
+
[Build Hash] Build time: 2025-10-11T09:01:01.521Z
|
|
1021
|
+
[Version Gen] No code changes - reusing previous version info
|
|
1022
|
+
...
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
### Benefits
|
|
1026
|
+
|
|
1027
|
+
1. **No Unnecessary Version Bumps**: Same code = same version
|
|
1028
|
+
2. **Automatic Versioning**: Patch version auto-increments on code changes
|
|
1029
|
+
3. **Build Traceability**: Hash + timestamp + git commit for debugging
|
|
1030
|
+
4. **Frontend Visibility**: Easy version checking in production
|
|
1031
|
+
5. **CI/CD Friendly**: Works in automated build pipelines
|
|
1032
|
+
|
|
1033
|
+
### Debugging
|
|
1034
|
+
|
|
1035
|
+
If you encounter issues:
|
|
1036
|
+
|
|
1037
|
+
1. **Check build metadata exists**:
|
|
1038
|
+
```bash
|
|
1039
|
+
cat dist/.build-metadata.json
|
|
1040
|
+
```
|
|
1041
|
+
|
|
1042
|
+
2. **Manually run scripts**:
|
|
1043
|
+
```bash
|
|
1044
|
+
node scripts/check-build-hash.cjs
|
|
1045
|
+
node scripts/generate-version-info.cjs
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
3. **Force new version**: Delete metadata file
|
|
1049
|
+
```bash
|
|
1050
|
+
rm dist/.build-metadata.json
|
|
1051
|
+
npm run build
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
4. **Check version.ts was generated**:
|
|
1055
|
+
```bash
|
|
1056
|
+
cat src/version.ts
|
|
1057
|
+
```
|
|
1058
|
+
|
|
1059
|
+
## File System
|
|
1060
|
+
|
|
1061
|
+
The file system is a separate collection of reference paths to assets and folders. It is designed this way so we can provide fast filterable/sortable virtual file systems across the platform that are organized and behave like a local file system. This allows us to replicate local file system functionality with complex file hierarchy.
|
|
1062
|
+
|
|
1063
|
+
### Key Features
|
|
1064
|
+
|
|
1065
|
+
- **Path-based organization**: Each file-system entry contains a reference to either an asset or folder as well as shared, indexed keys between assets and folders (keys that exist in both collections) like name, tags, etc.
|
|
1066
|
+
- **Fast searching**: This allows us to rapidly search for folders or assets with the same tag or partial-name matches for both folders and assets without using a complex and slow aggregation query.
|
|
1067
|
+
- **Multiple references**: Assets may have multiple file system entries. For example an asset may exist at `/project/:projectId/creator`, `/project/:projectId/reviewer` and `/submission/:submissionId` - each reference will point to the same asset.
|
|
1068
|
+
- **Unique folders**: Folders may only have one reference. If you copy a folder to a new location you will create a new folder (duplicating the original) and new file-system path.
|
|
1069
|
+
|
|
1070
|
+
### Path Structure
|
|
1071
|
+
|
|
1072
|
+
The path in the File-System object represents the path to but not including the file system item (Asset or Folder). So the path to asset-1 may be `/project/:projectId/creator` NOT `/project/:projectId/creator/asset-1`.
|
|
1073
|
+
|
|
1074
|
+
### SDK Support
|
|
1075
|
+
|
|
1076
|
+
The SDK now provides comprehensive file system operations through the `client.project` namespace:
|
|
1077
|
+
|
|
1078
|
+
- **Navigation**: `getItemsAtPath()` for browsing file system paths
|
|
1079
|
+
- **Organization**: `createFolder()`, `moveItemsToPath()`, `copyItemsToPath()`, `deleteItemsAtPath()`
|
|
1080
|
+
- **Publishing**: `publishItems()`, `unpublishItems()` with file system integration
|
|
1081
|
+
- **Search**: Advanced filtering by path, media type, tags, and name search
|