@auto-engineer/file-store 0.1.1 → 0.1.3
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +6 -11
- package/.turbo/turbo-type-check.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/src/{LightningFileStore.d.ts → InMemoryFileStore.d.ts} +6 -10
- package/dist/src/InMemoryFileStore.d.ts.map +1 -0
- package/dist/src/InMemoryFileStore.js +60 -0
- package/dist/src/InMemoryFileStore.js.map +1 -0
- package/dist/src/index.d.ts +0 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +0 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/path.d.ts +0 -2
- package/dist/src/path.d.ts.map +1 -1
- package/dist/src/path.js +0 -13
- package/dist/src/path.js.map +1 -1
- package/dist/src/types.d.ts +0 -7
- package/dist/src/types.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -30
- package/src/InMemoryFileStore.ts +68 -0
- package/src/index.ts +0 -2
- package/src/path.ts +0 -14
- package/src/types.ts +0 -9
- package/tsconfig.json +1 -1
- package/dist/src/FileWatcher.d.ts +0 -20
- package/dist/src/FileWatcher.d.ts.map +0 -1
- package/dist/src/FileWatcher.js +0 -70
- package/dist/src/FileWatcher.js.map +0 -1
- package/dist/src/LightningFileStore.d.ts.map +0 -1
- package/dist/src/LightningFileStore.js +0 -105
- package/dist/src/LightningFileStore.js.map +0 -1
- package/dist/src/LightningFileStore.specs.d.ts +0 -2
- package/dist/src/LightningFileStore.specs.d.ts.map +0 -1
- package/dist/src/LightningFileStore.specs.js +0 -86
- package/dist/src/LightningFileStore.specs.js.map +0 -1
- package/dist/src/text-helpers.d.ts +0 -7
- package/dist/src/text-helpers.d.ts.map +0 -1
- package/dist/src/text-helpers.js +0 -9
- package/dist/src/text-helpers.js.map +0 -1
- package/src/FileWatcher.ts +0 -73
- package/src/LightningFileStore.specs.ts +0 -109
- package/src/LightningFileStore.ts +0 -126
- package/src/text-helpers.ts +0 -16
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { IFileStore } from './types';
|
|
2
|
+
|
|
3
|
+
export class InMemoryFileStore implements IFileStore {
|
|
4
|
+
private files = new Map<string, Uint8Array>();
|
|
5
|
+
|
|
6
|
+
private norm(p: string) {
|
|
7
|
+
if (!p) return '/';
|
|
8
|
+
return p.startsWith('/') ? p : `/${p}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async write(path: string, data: Uint8Array): Promise<void> {
|
|
12
|
+
this.files.set(this.norm(path), new Uint8Array(data));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async read(path: string): Promise<Uint8Array | null> {
|
|
16
|
+
const buf = this.files.get(this.norm(path));
|
|
17
|
+
return buf ? new Uint8Array(buf) : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async remove(path: string): Promise<void> {
|
|
21
|
+
const prefix = this.norm(path);
|
|
22
|
+
for (const key of Array.from(this.files.keys())) {
|
|
23
|
+
if (key === prefix || key.startsWith(prefix.endsWith('/') ? prefix : prefix + '/')) {
|
|
24
|
+
this.files.delete(key);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async exists(path: string): Promise<boolean> {
|
|
30
|
+
const p = this.norm(path);
|
|
31
|
+
if (this.files.has(p)) return true;
|
|
32
|
+
for (const key of this.files.keys()) {
|
|
33
|
+
if (key.startsWith(p.endsWith('/') ? p : p + '/')) return true;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async listTree(root: string = '/'): Promise<Array<{ path: string; type: 'file' | 'dir'; size: number }>> {
|
|
39
|
+
const r = this.norm(root);
|
|
40
|
+
const files = Array.from(this.files.entries()).filter(
|
|
41
|
+
([p]) => p === r || p.startsWith(r.endsWith('/') ? r : r + '/'),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const dirs = new Set<string>(['/']);
|
|
45
|
+
for (const [p] of files) {
|
|
46
|
+
const parts = p.split('/').filter(Boolean);
|
|
47
|
+
let cur = '';
|
|
48
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
49
|
+
cur += '/' + parts[i];
|
|
50
|
+
dirs.add(cur || '/');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const out: Array<{ path: string; type: 'file' | 'dir'; size: number }> = [];
|
|
55
|
+
for (const d of Array.from(dirs)) {
|
|
56
|
+
if (d === '/' || d.startsWith(r.endsWith('/') ? r : r + '/')) {
|
|
57
|
+
out.push({ path: d, type: 'dir', size: 0 });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
for (const [p, buf] of files) {
|
|
62
|
+
out.push({ path: p, type: 'file', size: buf.byteLength });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
out.sort((a, b) => (a.type === b.type ? a.path.localeCompare(b.path) : a.type === 'dir' ? -1 : 1));
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/index.ts
CHANGED
package/src/path.ts
CHANGED
|
@@ -1,15 +1 @@
|
|
|
1
1
|
export const toPosix = (p: string) => p.replace(/\\/g, '/');
|
|
2
|
-
|
|
3
|
-
export const dirname = (p: string) => {
|
|
4
|
-
const n = toPosix(p);
|
|
5
|
-
const i = n.lastIndexOf('/');
|
|
6
|
-
return i <= 0 ? '/' : n.slice(0, i);
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
export const join = (...parts: string[]) => {
|
|
10
|
-
const filtered = parts.filter(Boolean);
|
|
11
|
-
if (!filtered.length) return '/';
|
|
12
|
-
const absolute = filtered[0].startsWith('/');
|
|
13
|
-
const joined = toPosix(filtered.map((s) => s.replace(/^\/+|\/+$/g, '')).join('/'));
|
|
14
|
-
return absolute ? `/${joined}` : joined;
|
|
15
|
-
};
|
package/src/types.ts
CHANGED
|
@@ -7,12 +7,3 @@ export interface IFileStore {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export type ChangeKind = 'created' | 'updated' | 'deleted';
|
|
10
|
-
|
|
11
|
-
export type FileEncoding = 'utf8' | 'base64';
|
|
12
|
-
|
|
13
|
-
export type FileChange = {
|
|
14
|
-
path: string;
|
|
15
|
-
kind: ChangeKind;
|
|
16
|
-
hash?: string;
|
|
17
|
-
size?: number;
|
|
18
|
-
};
|
package/tsconfig.json
CHANGED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'eventemitter3';
|
|
2
|
-
import type { FileChange, FileEncoding, IFileStore } from './types';
|
|
3
|
-
type Events = {
|
|
4
|
-
change: (c: FileChange) => void;
|
|
5
|
-
};
|
|
6
|
-
export declare class FileWatcher {
|
|
7
|
-
private store;
|
|
8
|
-
private bus;
|
|
9
|
-
private index;
|
|
10
|
-
constructor(store: IFileStore);
|
|
11
|
-
onChange(cb: (c: FileChange) => void): () => EventEmitter<Events, any>;
|
|
12
|
-
watch(glob: string | string[], cb: (c: FileChange) => void): () => EventEmitter<Events, any>;
|
|
13
|
-
writeFile(path: string, content: string | ArrayBuffer | Uint8Array, enc?: FileEncoding): Promise<void>;
|
|
14
|
-
deleteFile(path: string): Promise<void>;
|
|
15
|
-
seed(root?: string): Promise<void>;
|
|
16
|
-
private computeExistingHash;
|
|
17
|
-
private toBytes;
|
|
18
|
-
}
|
|
19
|
-
export {};
|
|
20
|
-
//# sourceMappingURL=FileWatcher.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FileWatcher.d.ts","sourceRoot":"","sources":["../../src/FileWatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,KAAK,EAAc,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAEhF,KAAK,MAAM,GAAG;IAAE,MAAM,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAA;CAAE,CAAC;AAElD,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,GAAG,CAA8B;IACzC,OAAO,CAAC,KAAK,CAA6B;gBAE9B,KAAK,EAAE,UAAU;IAI7B,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI;IAKpC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI;IAOpD,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,EAAE,GAAG,GAAE,YAAqB;IAW9F,UAAU,CAAC,IAAI,EAAE,MAAM;IAQvB,IAAI,CAAC,IAAI,SAAM;YAUP,mBAAmB;IASjC,OAAO,CAAC,OAAO;CAMhB"}
|
package/dist/src/FileWatcher.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'eventemitter3';
|
|
2
|
-
import picomatch from 'picomatch';
|
|
3
|
-
import md5 from 'md5';
|
|
4
|
-
export class FileWatcher {
|
|
5
|
-
constructor(store) {
|
|
6
|
-
this.bus = new EventEmitter();
|
|
7
|
-
this.index = new Map();
|
|
8
|
-
this.store = store;
|
|
9
|
-
}
|
|
10
|
-
onChange(cb) {
|
|
11
|
-
this.bus.on('change', cb);
|
|
12
|
-
return () => this.bus.off('change', cb);
|
|
13
|
-
}
|
|
14
|
-
watch(glob, cb) {
|
|
15
|
-
const isMatch = picomatch(glob, { dot: true });
|
|
16
|
-
return this.onChange((c) => {
|
|
17
|
-
if (isMatch(c.path))
|
|
18
|
-
cb(c);
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
async writeFile(path, content, enc = 'utf8') {
|
|
22
|
-
const data = this.toBytes(content, enc);
|
|
23
|
-
const prevHash = this.index.get(path) ?? (await this.computeExistingHash(path));
|
|
24
|
-
const nextHash = md5(data);
|
|
25
|
-
if (prevHash === nextHash)
|
|
26
|
-
return;
|
|
27
|
-
await this.store.write(path, data);
|
|
28
|
-
this.index.set(path, nextHash);
|
|
29
|
-
const kind = prevHash !== undefined ? 'updated' : 'created';
|
|
30
|
-
this.bus.emit('change', { path, kind, hash: nextHash, size: data.byteLength });
|
|
31
|
-
}
|
|
32
|
-
async deleteFile(path) {
|
|
33
|
-
const existed = await this.store.exists(path);
|
|
34
|
-
if (!existed)
|
|
35
|
-
return;
|
|
36
|
-
await this.store.remove(path);
|
|
37
|
-
this.index.delete(path);
|
|
38
|
-
this.bus.emit('change', { path, kind: 'deleted' });
|
|
39
|
-
}
|
|
40
|
-
async seed(root = '/') {
|
|
41
|
-
const entries = await this.store.listTree(root);
|
|
42
|
-
for (const e of entries) {
|
|
43
|
-
if (e.type === 'file') {
|
|
44
|
-
const buf = await this.store.read(e.path);
|
|
45
|
-
if (buf)
|
|
46
|
-
this.index.set(e.path, md5(buf));
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
async computeExistingHash(path) {
|
|
51
|
-
if (!(await this.store.exists(path)))
|
|
52
|
-
return undefined;
|
|
53
|
-
const buf = await this.store.read(path);
|
|
54
|
-
if (!buf)
|
|
55
|
-
return undefined;
|
|
56
|
-
const h = md5(buf);
|
|
57
|
-
this.index.set(path, h);
|
|
58
|
-
return h;
|
|
59
|
-
}
|
|
60
|
-
toBytes(content, enc) {
|
|
61
|
-
if (content instanceof Uint8Array)
|
|
62
|
-
return content;
|
|
63
|
-
if (content instanceof ArrayBuffer)
|
|
64
|
-
return new Uint8Array(content);
|
|
65
|
-
if (enc === 'base64')
|
|
66
|
-
return Uint8Array.from(atob(content), (c) => c.charCodeAt(0));
|
|
67
|
-
return new TextEncoder().encode(content);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
//# sourceMappingURL=FileWatcher.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"FileWatcher.js","sourceRoot":"","sources":["../../src/FileWatcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LightningFileStore.d.ts","sourceRoot":"","sources":["../../src/LightningFileStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAsB1C,qBAAa,kBAAmB,YAAW,UAAU;IACnD,OAAO,CAAC,EAAE,CAAsB;IAChC,OAAO,CAAC,GAAG,CAAsB;gBAErB,IAAI,SAAU;IAKpB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAQ9C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAOtC,QAAQ,CAAC,IAAI,GAAE,MAAY,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IA+BlG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAe3B,eAAe;IAS7B,OAAO,CAAC,mBAAmB;YAKb,MAAM;CAarB"}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import LightningFS from '@isomorphic-git/lightning-fs';
|
|
2
|
-
import { dirname } from './path.js';
|
|
3
|
-
export class LightningFileStore {
|
|
4
|
-
constructor(name = 'pocfs') {
|
|
5
|
-
this.fs = new LightningFS(name);
|
|
6
|
-
this.pfs = this.fs.promises;
|
|
7
|
-
}
|
|
8
|
-
async write(path, data) {
|
|
9
|
-
await this.mkdirp(dirname(path));
|
|
10
|
-
await this.pfs.writeFile(path, data);
|
|
11
|
-
}
|
|
12
|
-
async read(path) {
|
|
13
|
-
try {
|
|
14
|
-
return await this.pfs.readFile(path);
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
async exists(path) {
|
|
21
|
-
return await this.pfs
|
|
22
|
-
.stat(path)
|
|
23
|
-
.then(() => true)
|
|
24
|
-
.catch(() => false);
|
|
25
|
-
}
|
|
26
|
-
async listTree(root = '/') {
|
|
27
|
-
const out = [];
|
|
28
|
-
const walk = async (p) => {
|
|
29
|
-
let st = null;
|
|
30
|
-
try {
|
|
31
|
-
st = await this.pfs.stat(p);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (st !== null && st.isDirectory()) {
|
|
37
|
-
out.push({ path: p, type: 'dir', size: 0 });
|
|
38
|
-
let entries = [];
|
|
39
|
-
try {
|
|
40
|
-
entries = await this.pfs.readdir(p);
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
/* ignore */
|
|
44
|
-
}
|
|
45
|
-
for (const name of entries) {
|
|
46
|
-
const child = p === '/' ? `/${name}` : `${p}/${name}`;
|
|
47
|
-
await walk(child);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
out.push({ path: p, type: 'file', size: st?.size ?? 0 });
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
await walk(root);
|
|
55
|
-
out.sort((a, b) => (a.type === b.type ? a.path.localeCompare(b.path) : a.type === 'dir' ? -1 : 1));
|
|
56
|
-
return out;
|
|
57
|
-
}
|
|
58
|
-
async remove(path) {
|
|
59
|
-
if (!path)
|
|
60
|
-
return;
|
|
61
|
-
try {
|
|
62
|
-
const st = await this.pfs.stat(path);
|
|
63
|
-
if (st.isDirectory()) {
|
|
64
|
-
await this.removeDirectory(path);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
await this.pfs.unlink(path);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
if (this.isFileNotFoundError(err))
|
|
72
|
-
return;
|
|
73
|
-
throw err;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
async removeDirectory(path) {
|
|
77
|
-
const entries = await this.pfs.readdir(path).catch(() => []);
|
|
78
|
-
for (const name of entries) {
|
|
79
|
-
const child = path === '/' ? `/${name}` : `${path}/${name}`;
|
|
80
|
-
await this.remove(child);
|
|
81
|
-
}
|
|
82
|
-
await this.pfs.rmdir(path);
|
|
83
|
-
}
|
|
84
|
-
isFileNotFoundError(err) {
|
|
85
|
-
if (err instanceof Error && 'code' in err && err.code === 'ENOENT')
|
|
86
|
-
return true;
|
|
87
|
-
return err === null || err === undefined;
|
|
88
|
-
}
|
|
89
|
-
async mkdirp(path) {
|
|
90
|
-
if (!path || path === '/')
|
|
91
|
-
return;
|
|
92
|
-
const parts = path.split('/').filter(Boolean);
|
|
93
|
-
let cur = '';
|
|
94
|
-
for (const part of parts) {
|
|
95
|
-
cur += '/' + part;
|
|
96
|
-
const exists = await this.pfs
|
|
97
|
-
.stat(cur)
|
|
98
|
-
.then(() => true)
|
|
99
|
-
.catch(() => false);
|
|
100
|
-
if (!exists)
|
|
101
|
-
await this.pfs.mkdir(cur);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
//# sourceMappingURL=LightningFileStore.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LightningFileStore.js","sourceRoot":"","sources":["../../src/LightningFileStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LightningFileStore.specs.d.ts","sourceRoot":"","sources":["../../src/LightningFileStore.specs.ts"],"names":[],"mappings":"AAEA,OAAO,qBAAqB,CAAC"}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
3
|
-
import 'fake-indexeddb/auto';
|
|
4
|
-
import { LightningFileStore } from './LightningFileStore.js';
|
|
5
|
-
const te = new TextEncoder();
|
|
6
|
-
const td = new TextDecoder();
|
|
7
|
-
const resetIDBs = async () => await new Promise((res, rej) => {
|
|
8
|
-
const dbs = ['flowfs-test', 'flowfs-complex-test'];
|
|
9
|
-
let done = 0;
|
|
10
|
-
for (const name of dbs) {
|
|
11
|
-
const req = indexedDB.deleteDatabase(name);
|
|
12
|
-
req.onerror = () => rej(req.error);
|
|
13
|
-
req.onsuccess = () => {
|
|
14
|
-
done += 1;
|
|
15
|
-
if (done === dbs.length)
|
|
16
|
-
res();
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
beforeEach(async () => {
|
|
21
|
-
await resetIDBs();
|
|
22
|
-
});
|
|
23
|
-
describe('LightningFS FileStore (basic ops)', () => {
|
|
24
|
-
it('writes and reads a file', async () => {
|
|
25
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
26
|
-
await fs.write('/test.txt', te.encode('hello world'));
|
|
27
|
-
const buf = await fs.read('/test.txt');
|
|
28
|
-
expect(buf).not.toBeNull();
|
|
29
|
-
expect(td.decode(buf)).toBe('hello world');
|
|
30
|
-
});
|
|
31
|
-
it('checks file existence', async () => {
|
|
32
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
33
|
-
await fs.write('/exists.txt', te.encode('ok'));
|
|
34
|
-
expect(await fs.exists('/exists.txt')).toBe(true);
|
|
35
|
-
expect(await fs.exists('/missing.txt')).toBe(false);
|
|
36
|
-
});
|
|
37
|
-
it('supports nested directories (mkdir -p style) for write/read', async () => {
|
|
38
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
39
|
-
await fs.write('/nested/deep/file.txt', te.encode('nested content'));
|
|
40
|
-
const buf = await fs.read('/nested/deep/file.txt');
|
|
41
|
-
expect(buf).not.toBeNull();
|
|
42
|
-
expect(td.decode(buf)).toBe('nested content');
|
|
43
|
-
});
|
|
44
|
-
it('lists directory contents via listTree()', async () => {
|
|
45
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
46
|
-
await fs.write('/a.txt', te.encode('a'));
|
|
47
|
-
await fs.write('/dir/b.txt', te.encode('b'));
|
|
48
|
-
const tree = await fs.listTree('/');
|
|
49
|
-
const paths = tree.map((e) => e.path).sort();
|
|
50
|
-
expect(paths).toContain('/');
|
|
51
|
-
expect(paths).toContain('/a.txt');
|
|
52
|
-
expect(paths).toContain('/dir');
|
|
53
|
-
expect(paths).toContain('/dir/b.txt');
|
|
54
|
-
const root = tree.find((e) => e.path === '/');
|
|
55
|
-
const dir = tree.find((e) => e.path === '/dir');
|
|
56
|
-
const file = tree.find((e) => e.path === '/a.txt');
|
|
57
|
-
expect(root?.type).toBe('dir');
|
|
58
|
-
expect(dir?.type).toBe('dir');
|
|
59
|
-
expect(file?.type).toBe('file');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('LightningFS FileStore (complex structure)', () => {
|
|
63
|
-
it('creates and verifies a multi-level project layout', async () => {
|
|
64
|
-
const fs = new LightningFileStore('flowfs-complex-test');
|
|
65
|
-
await fs.write('/src/flows/items.flow.ts', te.encode('items flow'));
|
|
66
|
-
await fs.write('/src/flows/orders.flow.ts', te.encode('orders flow'));
|
|
67
|
-
await fs.write('/src/integrations/user.integration.ts', te.encode('user integration'));
|
|
68
|
-
await fs.write('/config/settings.json', te.encode('{"key":"value"}'));
|
|
69
|
-
expect(await fs.exists('/src/flows/items.flow.ts')).toBe(true);
|
|
70
|
-
expect(await fs.exists('/src/flows/orders.flow.ts')).toBe(true);
|
|
71
|
-
expect(await fs.exists('/src/integrations/user.integration.ts')).toBe(true);
|
|
72
|
-
expect(await fs.exists('/config/settings.json')).toBe(true);
|
|
73
|
-
const tree = await fs.listTree('/src');
|
|
74
|
-
const paths = tree.map((e) => e.path);
|
|
75
|
-
expect(paths).toContain('/src');
|
|
76
|
-
expect(paths).toContain('/src/flows');
|
|
77
|
-
expect(paths).toContain('/src/integrations');
|
|
78
|
-
expect(paths).toContain('/src/flows/items.flow.ts');
|
|
79
|
-
expect(paths).toContain('/src/flows/orders.flow.ts');
|
|
80
|
-
// verify content
|
|
81
|
-
const itemsBuf = await fs.read('/src/flows/items.flow.ts');
|
|
82
|
-
expect(itemsBuf).not.toBeNull();
|
|
83
|
-
expect(td.decode(itemsBuf)).toBe('items flow');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
//# sourceMappingURL=LightningFileStore.specs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"LightningFileStore.specs.js","sourceRoot":"","sources":["../../src/LightningFileStore.specs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export declare function readText(fs: {
|
|
2
|
-
read(path: string): Promise<Uint8Array | null>;
|
|
3
|
-
}, path: string): Promise<string | null>;
|
|
4
|
-
export declare function writeText(fs: {
|
|
5
|
-
write(path: string, data: Uint8Array): Promise<void>;
|
|
6
|
-
}, path: string, text: string): Promise<void>;
|
|
7
|
-
//# sourceMappingURL=text-helpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text-helpers.d.ts","sourceRoot":"","sources":["../../src/text-helpers.ts"],"names":[],"mappings":"AAAA,wBAAsB,QAAQ,CAC5B,EAAE,EAAE;IAAE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;CAAE,EACtD,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,SAAS,CAC7B,EAAE,EAAE;IAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,EAC5D,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
package/dist/src/text-helpers.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export async function readText(fs, path) {
|
|
2
|
-
const buf = await fs.read(path);
|
|
3
|
-
return buf ? new TextDecoder().decode(buf) : null;
|
|
4
|
-
}
|
|
5
|
-
export async function writeText(fs, path, text) {
|
|
6
|
-
const buf = new TextEncoder().encode(text);
|
|
7
|
-
await fs.write(path, buf);
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=text-helpers.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text-helpers.js","sourceRoot":"","sources":["../../src/text-helpers.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,EAAsD,EACtD,IAAY;IAEZ,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,EAA4D,EAC5D,IAAY,EACZ,IAAY;IAEZ,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AAC5B,CAAC"}
|
package/src/FileWatcher.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from 'eventemitter3';
|
|
2
|
-
import picomatch from 'picomatch';
|
|
3
|
-
import md5 from 'md5';
|
|
4
|
-
import type { ChangeKind, FileChange, FileEncoding, IFileStore } from './types';
|
|
5
|
-
|
|
6
|
-
type Events = { change: (c: FileChange) => void };
|
|
7
|
-
|
|
8
|
-
export class FileWatcher {
|
|
9
|
-
private store: IFileStore;
|
|
10
|
-
private bus = new EventEmitter<Events>();
|
|
11
|
-
private index = new Map<string, string>();
|
|
12
|
-
|
|
13
|
-
constructor(store: IFileStore) {
|
|
14
|
-
this.store = store;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
onChange(cb: (c: FileChange) => void) {
|
|
18
|
-
this.bus.on('change', cb);
|
|
19
|
-
return () => this.bus.off('change', cb);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
watch(glob: string | string[], cb: (c: FileChange) => void) {
|
|
23
|
-
const isMatch = picomatch(glob, { dot: true });
|
|
24
|
-
return this.onChange((c) => {
|
|
25
|
-
if (isMatch(c.path)) cb(c);
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async writeFile(path: string, content: string | ArrayBuffer | Uint8Array, enc: FileEncoding = 'utf8') {
|
|
30
|
-
const data = this.toBytes(content, enc);
|
|
31
|
-
const prevHash = this.index.get(path) ?? (await this.computeExistingHash(path));
|
|
32
|
-
const nextHash = md5(data);
|
|
33
|
-
if (prevHash === nextHash) return;
|
|
34
|
-
await this.store.write(path, data);
|
|
35
|
-
this.index.set(path, nextHash);
|
|
36
|
-
const kind: ChangeKind = prevHash !== undefined ? 'updated' : 'created';
|
|
37
|
-
this.bus.emit('change', { path, kind, hash: nextHash, size: data.byteLength });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async deleteFile(path: string) {
|
|
41
|
-
const existed = await this.store.exists(path);
|
|
42
|
-
if (!existed) return;
|
|
43
|
-
await this.store.remove(path);
|
|
44
|
-
this.index.delete(path);
|
|
45
|
-
this.bus.emit('change', { path, kind: 'deleted' });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async seed(root = '/') {
|
|
49
|
-
const entries = await this.store.listTree(root);
|
|
50
|
-
for (const e of entries) {
|
|
51
|
-
if (e.type === 'file') {
|
|
52
|
-
const buf = await this.store.read(e.path);
|
|
53
|
-
if (buf) this.index.set(e.path, md5(buf));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private async computeExistingHash(path: string) {
|
|
59
|
-
if (!(await this.store.exists(path))) return undefined;
|
|
60
|
-
const buf = await this.store.read(path);
|
|
61
|
-
if (!buf) return undefined;
|
|
62
|
-
const h = md5(buf);
|
|
63
|
-
this.index.set(path, h);
|
|
64
|
-
return h;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private toBytes(content: string | ArrayBuffer | Uint8Array, enc: FileEncoding): Uint8Array {
|
|
68
|
-
if (content instanceof Uint8Array) return content;
|
|
69
|
-
if (content instanceof ArrayBuffer) return new Uint8Array(content);
|
|
70
|
-
if (enc === 'base64') return Uint8Array.from(atob(content), (c) => c.charCodeAt(0));
|
|
71
|
-
return new TextEncoder().encode(content);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
// @vitest-environment jsdom
|
|
2
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
3
|
-
import 'fake-indexeddb/auto';
|
|
4
|
-
import { LightningFileStore } from './LightningFileStore';
|
|
5
|
-
|
|
6
|
-
const te = new TextEncoder();
|
|
7
|
-
const td = new TextDecoder();
|
|
8
|
-
|
|
9
|
-
const resetIDBs = async () =>
|
|
10
|
-
await new Promise<void>((res, rej) => {
|
|
11
|
-
const dbs = ['flowfs-test', 'flowfs-complex-test'];
|
|
12
|
-
let done = 0;
|
|
13
|
-
for (const name of dbs) {
|
|
14
|
-
const req = indexedDB.deleteDatabase(name);
|
|
15
|
-
req.onerror = () => rej(req.error);
|
|
16
|
-
req.onsuccess = () => {
|
|
17
|
-
done += 1;
|
|
18
|
-
if (done === dbs.length) res();
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
beforeEach(async () => {
|
|
24
|
-
await resetIDBs();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('LightningFS FileStore (basic ops)', () => {
|
|
28
|
-
it('writes and reads a file', async () => {
|
|
29
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
30
|
-
|
|
31
|
-
await fs.write('/test.txt', te.encode('hello world'));
|
|
32
|
-
const buf = await fs.read('/test.txt');
|
|
33
|
-
|
|
34
|
-
expect(buf).not.toBeNull();
|
|
35
|
-
expect(td.decode(buf!)).toBe('hello world');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('checks file existence', async () => {
|
|
39
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
40
|
-
|
|
41
|
-
await fs.write('/exists.txt', te.encode('ok'));
|
|
42
|
-
|
|
43
|
-
expect(await fs.exists('/exists.txt')).toBe(true);
|
|
44
|
-
expect(await fs.exists('/missing.txt')).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('supports nested directories (mkdir -p style) for write/read', async () => {
|
|
48
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
49
|
-
|
|
50
|
-
await fs.write('/nested/deep/file.txt', te.encode('nested content'));
|
|
51
|
-
const buf = await fs.read('/nested/deep/file.txt');
|
|
52
|
-
|
|
53
|
-
expect(buf).not.toBeNull();
|
|
54
|
-
expect(td.decode(buf!)).toBe('nested content');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('lists directory contents via listTree()', async () => {
|
|
58
|
-
const fs = new LightningFileStore('flowfs-test');
|
|
59
|
-
|
|
60
|
-
await fs.write('/a.txt', te.encode('a'));
|
|
61
|
-
await fs.write('/dir/b.txt', te.encode('b'));
|
|
62
|
-
|
|
63
|
-
const tree = await fs.listTree('/');
|
|
64
|
-
|
|
65
|
-
const paths = tree.map((e) => e.path).sort();
|
|
66
|
-
expect(paths).toContain('/');
|
|
67
|
-
expect(paths).toContain('/a.txt');
|
|
68
|
-
expect(paths).toContain('/dir');
|
|
69
|
-
expect(paths).toContain('/dir/b.txt');
|
|
70
|
-
|
|
71
|
-
const root = tree.find((e) => e.path === '/');
|
|
72
|
-
const dir = tree.find((e) => e.path === '/dir');
|
|
73
|
-
const file = tree.find((e) => e.path === '/a.txt');
|
|
74
|
-
|
|
75
|
-
expect(root?.type).toBe('dir');
|
|
76
|
-
expect(dir?.type).toBe('dir');
|
|
77
|
-
expect(file?.type).toBe('file');
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
describe('LightningFS FileStore (complex structure)', () => {
|
|
82
|
-
it('creates and verifies a multi-level project layout', async () => {
|
|
83
|
-
const fs = new LightningFileStore('flowfs-complex-test');
|
|
84
|
-
|
|
85
|
-
await fs.write('/src/flows/items.flow.ts', te.encode('items flow'));
|
|
86
|
-
await fs.write('/src/flows/orders.flow.ts', te.encode('orders flow'));
|
|
87
|
-
await fs.write('/src/integrations/user.integration.ts', te.encode('user integration'));
|
|
88
|
-
await fs.write('/config/settings.json', te.encode('{"key":"value"}'));
|
|
89
|
-
|
|
90
|
-
expect(await fs.exists('/src/flows/items.flow.ts')).toBe(true);
|
|
91
|
-
expect(await fs.exists('/src/flows/orders.flow.ts')).toBe(true);
|
|
92
|
-
expect(await fs.exists('/src/integrations/user.integration.ts')).toBe(true);
|
|
93
|
-
expect(await fs.exists('/config/settings.json')).toBe(true);
|
|
94
|
-
|
|
95
|
-
const tree = await fs.listTree('/src');
|
|
96
|
-
const paths = tree.map((e) => e.path);
|
|
97
|
-
|
|
98
|
-
expect(paths).toContain('/src');
|
|
99
|
-
expect(paths).toContain('/src/flows');
|
|
100
|
-
expect(paths).toContain('/src/integrations');
|
|
101
|
-
expect(paths).toContain('/src/flows/items.flow.ts');
|
|
102
|
-
expect(paths).toContain('/src/flows/orders.flow.ts');
|
|
103
|
-
|
|
104
|
-
// verify content
|
|
105
|
-
const itemsBuf = await fs.read('/src/flows/items.flow.ts');
|
|
106
|
-
expect(itemsBuf).not.toBeNull();
|
|
107
|
-
expect(td.decode(itemsBuf!)).toBe('items flow');
|
|
108
|
-
});
|
|
109
|
-
});
|