13.1 KB395 lines
Blame
1/**
2 * This software contains information and intellectual property that is
3 * confidential and proprietary to Facebook, Inc. and its affiliates.
4 *
5 * @generated
6 */
7
8/*
9 * This file is synced between fbcode/eden/fs/facebook/prototypes/node-edenfs-notifications-client/example.js.
10 * The authoritative copy is the one in eden/fs/.
11 * Use `yarn sync-edenfs-notifications` to perform the sync.
12 *
13 * This file is intended to be self contained so it may be copied/referenced from other extensions,
14 * which is why it should not import anything and why it reimplements many types.
15 */
16
17/**
18 * Example usage of the EdenFS Notify JavaScript interface
19 */
20
21const {EdenFSNotificationsClient, EdenFSUtils} = require('./index.js');
22
23async function basicExample() {
24 console.log('=== Basic EdenFS Notify Example ===');
25
26 // Create a client instance
27 const client = new EdenFSNotificationsClient({
28 mountPoint: null,
29 timeout: 1000, // 1 second timeout
30 edenBinaryPath: process.env.EDEN_PATH ? process.env.EDEN_PATH : 'eden',
31 });
32
33 try {
34 // Get current journal position
35 console.log('Getting current journal position...');
36 const position = await client.getPosition();
37 console.log('Current position:', position);
38 } catch (error) {
39 console.error('Error:', error.message);
40 }
41}
42
43async function waitReadyExample() {
44 console.log('\n=== EdenFS Wait Ready Example ===');
45
46 // Create a client instance with a short timeout for demonstration
47 const client = new EdenFSNotificationsClient({
48 mountPoint: null,
49 timeout: 5000, // 5 second default timeout
50 edenBinaryPath: process.env.EDEN_PATH ? process.env.EDEN_PATH : 'eden',
51 });
52
53 try {
54 // Wait for EdenFS to be ready (useful after restart or initial setup)
55 console.log('Waiting for EdenFS to be ready...');
56 const isReady = await client.waitReady({
57 timeout: 10000, // Wait up to 10 seconds
58 });
59
60 if (isReady) {
61 console.log('EdenFS is ready!');
62 // Now safe to perform operations
63 const position = await client.getPosition();
64 console.log('Current position:', position);
65 } else {
66 console.log('EdenFS did not become ready within timeout');
67 }
68 } catch (error) {
69 console.error('Error:', error.message);
70 }
71}
72
73async function changesExample(position) {
74 console.log('=== EdenFS Notify changesSince Example ===');
75
76 // Create a client instance
77 const client = new EdenFSNotificationsClient({
78 mountPoint: null,
79 timeout: 1000, // 1 second timeout
80 edenBinaryPath: process.env.EDEN_PATH ? process.env.EDEN_PATH : 'eden',
81 });
82
83 try {
84 // Get changes since a specific position (if you have one)
85 console.log('\nGetting recent changes...');
86 const changes = await client.getChangesSince({
87 position: position, // Start from current position
88 });
89 console.log('Changes:', JSON.stringify(changes, null, 2));
90
91 // Extract file paths from changes
92 if (changes.changes && changes.changes.length > 0) {
93 const paths = EdenFSUtils.extractPaths(changes.changes);
94 console.log('Changed files:', paths);
95 }
96 } catch (error) {
97 console.error('Error:', error.message);
98 }
99}
100
101async function subscriptionExample() {
102 console.log('\n=== Subscription Example ===');
103
104 const client = new EdenFSNotificationsClient({
105 // mountPoint: '/path/to/your/eden/mount' // Replace with your actual mount point
106 edenBinaryPath: process.env.EDEN_PATH ? process.env.EDEN_PATH : 'eden',
107 });
108
109 try {
110 // Create a subscription for real-time changes
111 const subscription = client.subscribe(
112 {
113 throttle: 100, // 100ms throttle between events
114 includedSuffixes: ['.js', '.ts', '.py'], // Only watch specific file types
115 excludedRoots: ['node_modules', '.git'], // Exclude certain directories
116 deferredStates: ['test'], // Wait for these states to be deasserted
117 },
118 (error, resp) => {
119 if (error) {
120 console.error('Subscription error:', error.message);
121 return;
122 } else if (resp === null) {
123 console.error('Subscription closed');
124 return;
125 } else {
126 console.log('\n--- File System Change Detected ---');
127 if (resp.to_position) {
128 console.log('Position:', resp.to_position);
129 } else if (resp.position) {
130 console.log('Position:', resp.position);
131 } else {
132 console.error('Unknown response');
133 }
134
135 if (resp.changes && resp.changes.length > 0) {
136 resp.changes.forEach(change => {
137 const changeType = EdenFSUtils.getChangeType(change);
138 if (change.SmallChange) {
139 const paths = EdenFSUtils.extractPaths([change]);
140 console.log(`${changeType.toUpperCase()}: ${paths.join(', ')}`);
141 } else if (change.LargeChange) {
142 if (changeType == 'directory renamed') {
143 console.log(
144 `${changeType.toUpperCase()}: From ${EdenFSUtils.bytesToPath(change.LargeChange.DirectoryRenamed.from)} to ${EdenFSUtils.bytesToPath(change.LargeChange.DirectoryRenamed.to)}`,
145 );
146 } else if (changeType == 'commit transition') {
147 console.log(
148 `${changeType.toUpperCase()}: From ${EdenFSUtils.bytesToHex(change.LargeChange.CommitTransition.from)} to ${EdenFSUtils.bytesToHex(change.LargeChange.CommitTransition.to)}`,
149 );
150 } else if (changeType == 'lost changes') {
151 console.log(
152 `${changeType.toUpperCase()}: ${change.LargeChange.LostChanges.reason}`,
153 );
154 } else {
155 console.log(`Unknown large change: ${JSON.stringify(change)}`);
156 }
157 }
158 });
159 } else if (resp.state) {
160 console.log(`State change: ${resp.event_type} ${resp.state}`);
161 } else {
162 console.error(`Unknown response: ${JSON.stringify(resp)}`);
163 }
164 }
165 },
166 );
167
168 // Start the subscription
169 console.log('Starting subscription...');
170 await subscription.start();
171 console.log('Subscription active. Make some file changes to see events.');
172 console.log('Press Ctrl+C to stop.');
173
174 // Keep the process running
175 process.on('SIGINT', async () => {
176 console.log('\nStopping subscription...');
177 // Wait until the subscription has fully exited before terminating
178 subscription.on('exit', () => {
179 console.log('Subscription exited');
180 process.exit(0);
181 });
182 subscription.stop();
183 });
184
185 // Prevent the script from exiting
186 await new Promise(() => {});
187 } catch (error) {
188 console.error('Subscription error:', error.message);
189 }
190}
191
192async function stateExample() {
193 console.log('\n=== State Management Example ===');
194
195 const client = new EdenFSNotificationsClient({
196 // mountPoint: '/path/to/your/eden/mount' // Replace with your actual mount point
197 edenBinaryPath: process.env.EDEN_PATH ? process.env.EDEN_PATH : 'eden',
198 });
199
200 try {
201 // Enter a state for 10 seconds
202 console.log('Entering "build" state for 10 seconds...');
203 await client.enterState('build', {duration: 10});
204 console.log('State entered successfully');
205 } catch (error) {
206 console.error('State error:', error.message);
207 }
208}
209
210async function advancedSubscriptionExample() {
211 console.log('\n=== Advanced Subscription with States ===');
212
213 const client = new EdenFSNotificationsClient({
214 // mountPoint: '/path/to/your/eden/mount' // Replace with your actual mount point
215 edenBinaryPath: process.env.EDEN_PATH ? process.env.EDEN_PATH : 'eden',
216 });
217
218 try {
219 // Create a subscription that waits for certain states to be deasserted
220 const subscription = client.subscribe(
221 {
222 deferredStates: ['build', 'test'], // Wait for these states to be deasserted
223 throttle: 50,
224 includedSuffixes: ['.js', '.ts', '.json'],
225 },
226 (error, resp) => {
227 if (error) {
228 console.error('Subscription error:', error.message);
229 return;
230 }
231
232 console.log('\n--- File System Change Detected ---');
233 if (resp.to_position) {
234 console.log('Position:', resp.to_position);
235 } else if (resp.position) {
236 console.log('Position:', resp.position);
237 } else {
238 console.error('Unknown response: ', resp);
239 }
240
241 if (resp.changes) {
242 if (resp.changes.length === 0) {
243 console.log('no changes');
244 }
245 resp.changes.forEach(change => {
246 const changeType = EdenFSUtils.getChangeType(change);
247 if (change.SmallChange) {
248 const paths = EdenFSUtils.extractPaths([change]);
249 console.log(`${changeType.toUpperCase()}: ${paths.join(', ')}`);
250 } else if (change.LargeChange) {
251 if (changeType == 'directory renamed') {
252 console.log(
253 `${changeType.toUpperCase()}: From ${EdenFSUtils.bytesToPath(change.LargeChange.DirectoryRenamed.from)} to ${EdenFSUtils.bytesToPath(change.LargeChange.DirectoryRenamed.to)}`,
254 );
255 } else if (changeType == 'commit transition') {
256 console.log(
257 `${changeType.toUpperCase()}: From ${EdenFSUtils.bytesToHex(change.LargeChange.CommitTransition.from)} to ${EdenFSUtils.bytesToHex(change.LargeChange.CommitTransition.to)}`,
258 );
259 } else if (changeType == 'lost changes') {
260 console.log(
261 `${changeType.toUpperCase()}: ${change.LargeChange.LostChanges.reason}`,
262 );
263 } else {
264 console.log(`Unknown large change: ${JSON.stringify(change)}`);
265 }
266 }
267 });
268 } else if (resp.state) {
269 console.log(`State change: ${resp.event_type} ${resp.state}`);
270 } else {
271 console.error(`Unknown response: ${JSON.stringify(resp)}`);
272 }
273 },
274 );
275
276 await subscription.start();
277 console.log('Advanced subscription started. Try entering/exiting states.');
278
279 // Simulate entering and exiting states
280 setTimeout(async () => {
281 console.log('\nEntering build state...');
282 await client.enterState('build', {duration: 5});
283 }, 2000);
284
285 setTimeout(async () => {
286 console.log('\nEntering test state...');
287 await client.enterState('test', {duration: 3});
288 }, 8000);
289
290 // Keep running for demo
291 setTimeout(() => {
292 subscription.stop();
293 console.log('\nDemo completed');
294 process.exit(0);
295 }, 15000);
296 } catch (error) {
297 console.error('Advanced subscription error:', error.message);
298 }
299}
300
301async function utilityExample() {
302 console.log('\n=== Utility Functions Example ===');
303
304 // Example change data (as returned by EdenFS)
305 const exampleChanges = [
306 {
307 SmallChange: {
308 Added: {
309 file_type: 'Regular',
310 path: [104, 101, 108, 108, 111, 46, 116, 120, 116], // "hello.txt" in bytes
311 },
312 },
313 },
314 {
315 SmallChange: {
316 Modified: {
317 file_type: 'Regular',
318 path: [119, 111, 114, 108, 100, 46, 106, 115], // "world.js" in bytes
319 },
320 },
321 },
322 {
323 SmallChange: {
324 Renamed: {
325 file_type: 'Regular',
326 from: [111, 108, 100, 46, 116, 120, 116], // "old.txt" in bytes
327 to: [110, 101, 119, 46, 116, 120, 116], // "new.txt" in bytes
328 },
329 },
330 },
331 {
332 LargeChange: {
333 CommitTransition: {
334 from: [111, 108, 100, 46, 116, 120, 116], // "old.txt" in bytes
335 to: [110, 101, 119, 46, 116, 120, 116], // "new.txt" in bytes
336 },
337 },
338 },
339 {
340 StateChange: {
341 StateEntered: {
342 name: 'meerkat',
343 },
344 },
345 },
346 ];
347
348 // Extract paths from changes
349 console.log('Extracting single path');
350 const [path1, path2] = EdenFSUtils.extractPath(exampleChanges[0].SmallChange);
351 console.log('Extracted paths:', path1, path2);
352 console.log('Extracting multiple paths:');
353 const paths = EdenFSUtils.extractPaths(exampleChanges);
354 console.log('Extracted paths:', paths);
355 console.log('Extracting types:');
356 const type = EdenFSUtils.extractFileType(exampleChanges[0].SmallChange);
357 console.log('Extracted file type:', type);
358
359 // Get change types
360 exampleChanges.forEach((change, index) => {
361 const changeType = EdenFSUtils.getChangeType(change);
362 console.log(`Change ${index + 1} type:`, changeType);
363 });
364}
365
366// Run examples
367async function runExamples() {
368 console.log('EdenFS Notify JavaScript Interface Examples');
369 console.log('==========================================');
370
371 // Note: Update the mount point in each example before running
372 console.log('\nNOTE: Please update the mount point paths in the examples before running!');
373
374 try {
375 await basicExample();
376 await waitReadyExample();
377 if (process.argv.length > 2) {
378 await changesExample(process.argv[2]);
379 }
380 await utilityExample();
381
382 // Uncomment these to run interactive examples:
383 // await subscriptionExample();
384 // await stateExample();
385 // await advancedSubscriptionExample();
386 } catch (error) {
387 console.error('Example error:', error.message);
388 }
389}
390
391// Run if this file is executed directly
392if (require.main === module) {
393 runExamples();
394}
395