| 110 | } |
| 111 | |
| 112 | export class SQLiteWorkspaceProvider { |
| 113 | readonly db: Database; |
| 114 | readonly now: () => number; |
| 115 | |
| 116 | // Capability flags consulted by @platformatic/vfs callers. |
| 117 | readonly readonly = false; |
| 118 | readonly supportsSymlinks = true; |
| 119 | readonly supportsWatch = true; |
| 120 | |
| 121 | // Fd table. Start at 3 — 0/1/2 are reserved by convention even |
| 122 | // though we don't expose them — so consumers that pass them around |
| 123 | // can't accidentally collide with stdio mental models. |
| 124 | #fds = new Map<number, FdState>(); |
| 125 | #nextFd = 3; |
| 126 | |
| 127 | readonly watchIntervalMs: number; |
| 128 | |
| 129 | constructor(db: Database, options: SQLiteWorkspaceProviderOptions = {}) { |
| 130 | this.db = db; |
| 131 | this.now = options.now ?? Date.now; |
| 132 | this.watchIntervalMs = options.watchIntervalMs ?? 100; |
| 133 | } |
| 134 | |
| 135 | // -- Essential primitives ------------------------------------------ |
| 136 | |
| 137 | open(path: string, flags?: string, mode?: number): Promise<number> { |
| 138 | return Promise.resolve(this.openSync(path, flags, mode)); |
| 139 | } |
| 140 | |
| 141 | openSync(path: string, flags: string = "r", _mode?: number): number { |
| 142 | const { read, write, truncate, append, create, exclusive } = parseFlags(flags); |
| 143 | const existing = resolveInode(this.db, path); |
| 144 | |
| 145 | if (existing === null) { |
| 146 | if (!create) { |
| 147 | throw createWorkspaceError("ENOENT", `no such file: ${path}`, path); |
| 148 | } |
| 149 | writeFileSyncImpl(this.db, path, new Uint8Array(), {}, this.now); |
| 150 | } else { |
| 151 | if (existing.type !== "file") { |
| 152 | throw createWorkspaceError("EISDIR", `path is a directory: ${path}`, path); |
| 153 | } |
| 154 | if (exclusive) { |
| 155 | throw createWorkspaceError("EEXIST", `path exists: ${path}`, path); |
| 156 | } |
| 157 | if (truncate) { |
| 158 | writeFileSyncImpl(this.db, path, new Uint8Array(), {}, this.now); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | const stat = statImpl(this.db, path); |
| 163 | const fd = this.#nextFd++; |
| 164 | this.#fds.set(fd, { |
| 165 | path, |
| 166 | position: append ? stat.size : 0, |
| 167 | readable: read, |
| 168 | writable: write, |
| 169 | append, |
nothing calls this directly
no outgoing calls
no test coverage detected