| 83 | const SYNC_TICK_MS = 250; |
| 84 | |
| 85 | export async function createNodeVirtualFileSystem( |
| 86 | options: CreateOptions = {}, |
| 87 | ): Promise<NodeVfsHandle> { |
| 88 | ensureVirtualProviderPrototype(); |
| 89 | const storage = new SQLiteTestStorage(); |
| 90 | const db = new Database(storage); |
| 91 | initializeSchema(db, () => Date.now()); |
| 92 | |
| 93 | let stopSync = () => {}; |
| 94 | if (options.upstream !== undefined) { |
| 95 | // Initial pull. The polling loop would catch up eventually, |
| 96 | // but the FUSE mount comes up populated by waiting for the |
| 97 | // first pull to settle before returning. |
| 98 | await pullOnce(db, options.upstream); |
| 99 | stopSync = startSyncLoop(db, options.upstream); |
| 100 | } |
| 101 | |
| 102 | const provider = new SQLiteWorkspaceProvider(db); |
| 103 | const vfs = create(provider as unknown as VirtualProvider, { moduleHooks: false }); |
| 104 | // Forward the extra dofs methods that @platformatic/vfs's |
| 105 | // VirtualFileSystem doesn't expose. We bind directly to the |
| 106 | // provider — there is no `inner` indirection — so dispatch can't |
| 107 | // silently fall off if a method name only exists on one side. |
| 108 | // biome-ignore lint/suspicious/noExplicitAny: untyped extension surface |
| 109 | const providerAny = provider as any; |
| 110 | for (const name of EXTRA_VFS_METHODS) { |
| 111 | const fn = providerAny[name]; |
| 112 | if (typeof fn !== "function") continue; |
| 113 | Object.defineProperty(vfs, name, { |
| 114 | // biome-ignore lint/suspicious/noExplicitAny: untyped extension surface |
| 115 | value: (...args: any[]) => fn.apply(providerAny, args), |
| 116 | writable: true, |
| 117 | configurable: true, |
| 118 | }); |
| 119 | } |
| 120 | return { vfs, db, stopSync }; |
| 121 | } |
| 122 | |
| 123 | // Drive tick(db, upstream) on a setInterval. Errors during a tick |
| 124 | // log and continue — a transient upstream failure shouldn't |