@effect-vfs/core 0.0.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 +21 -0
- package/README.md +283 -0
- package/dist/Snapshot.d.ts +65 -0
- package/dist/Snapshot.d.ts.map +1 -0
- package/dist/Snapshot.js +43 -0
- package/dist/VirtualFileSystem.d.ts +674 -0
- package/dist/VirtualFileSystem.d.ts.map +1 -0
- package/dist/VirtualFileSystem.js +1768 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/internal/image.d.ts +142 -0
- package/dist/internal/image.d.ts.map +1 -0
- package/dist/internal/image.js +187 -0
- package/package.json +53 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-neutral virtual filesystem contracts and constructors.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.1.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Virtual filesystem contracts, schemas, errors, and constructors.
|
|
8
|
+
*
|
|
9
|
+
* @category exports
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
export * as VirtualFileSystem from "./VirtualFileSystem.js";
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;GAKG;AACH,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime-neutral virtual filesystem contracts and constructors.
|
|
3
|
+
*
|
|
4
|
+
* @since 0.1.0
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Virtual filesystem contracts, schemas, errors, and constructors.
|
|
8
|
+
*
|
|
9
|
+
* @category exports
|
|
10
|
+
* @since 0.1.0
|
|
11
|
+
*/
|
|
12
|
+
export * as VirtualFileSystem from "./VirtualFileSystem.js";
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot validation and serialization used by `VirtualFileSystem`.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
8
|
+
import { ImageError, type Snapshot } from "../Snapshot.js";
|
|
9
|
+
/** @internal */
|
|
10
|
+
export declare const StoredMetadata: Schema.Struct<{
|
|
11
|
+
readonly uid: Schema.Finite;
|
|
12
|
+
readonly gid: Schema.Finite;
|
|
13
|
+
readonly mode: Schema.Finite;
|
|
14
|
+
readonly atimeNs: Schema.String;
|
|
15
|
+
readonly mtimeNs: Schema.String;
|
|
16
|
+
readonly ctimeNs: Schema.String;
|
|
17
|
+
readonly birthtimeNs: Schema.String;
|
|
18
|
+
}>;
|
|
19
|
+
/** @internal */
|
|
20
|
+
export type StoredMetadata = typeof StoredMetadata.Type;
|
|
21
|
+
/** @internal */
|
|
22
|
+
export declare const Record: Schema.Union<readonly [Schema.Struct<{
|
|
23
|
+
readonly id: Schema.String;
|
|
24
|
+
readonly kind: Schema.Literal<"directory">;
|
|
25
|
+
readonly metadata: Schema.Struct<{
|
|
26
|
+
readonly uid: Schema.Finite;
|
|
27
|
+
readonly gid: Schema.Finite;
|
|
28
|
+
readonly mode: Schema.Finite;
|
|
29
|
+
readonly atimeNs: Schema.String;
|
|
30
|
+
readonly mtimeNs: Schema.String;
|
|
31
|
+
readonly ctimeNs: Schema.String;
|
|
32
|
+
readonly birthtimeNs: Schema.String;
|
|
33
|
+
}>;
|
|
34
|
+
readonly entries: Schema.$Array<Schema.Struct<{
|
|
35
|
+
readonly name: Schema.String;
|
|
36
|
+
readonly target: Schema.String;
|
|
37
|
+
}>>;
|
|
38
|
+
}>, Schema.Struct<{
|
|
39
|
+
readonly id: Schema.String;
|
|
40
|
+
readonly kind: Schema.Literal<"file">;
|
|
41
|
+
readonly metadata: Schema.Struct<{
|
|
42
|
+
readonly uid: Schema.Finite;
|
|
43
|
+
readonly gid: Schema.Finite;
|
|
44
|
+
readonly mode: Schema.Finite;
|
|
45
|
+
readonly atimeNs: Schema.String;
|
|
46
|
+
readonly mtimeNs: Schema.String;
|
|
47
|
+
readonly ctimeNs: Schema.String;
|
|
48
|
+
readonly birthtimeNs: Schema.String;
|
|
49
|
+
}>;
|
|
50
|
+
readonly data: Schema.String;
|
|
51
|
+
}>, Schema.Struct<{
|
|
52
|
+
readonly id: Schema.String;
|
|
53
|
+
readonly kind: Schema.Literal<"symlink">;
|
|
54
|
+
readonly metadata: Schema.Struct<{
|
|
55
|
+
readonly uid: Schema.Finite;
|
|
56
|
+
readonly gid: Schema.Finite;
|
|
57
|
+
readonly mode: Schema.Finite;
|
|
58
|
+
readonly atimeNs: Schema.String;
|
|
59
|
+
readonly mtimeNs: Schema.String;
|
|
60
|
+
readonly ctimeNs: Schema.String;
|
|
61
|
+
readonly birthtimeNs: Schema.String;
|
|
62
|
+
}>;
|
|
63
|
+
readonly target: Schema.String;
|
|
64
|
+
}>]>;
|
|
65
|
+
/** @internal */
|
|
66
|
+
export type Record = typeof Record.Type;
|
|
67
|
+
/** @internal */
|
|
68
|
+
export declare const Document: Schema.Struct<{
|
|
69
|
+
readonly format: Schema.Literal<"effect-vfs">;
|
|
70
|
+
readonly version: Schema.Literal<1>;
|
|
71
|
+
readonly root: Schema.String;
|
|
72
|
+
readonly records: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
73
|
+
readonly id: Schema.String;
|
|
74
|
+
readonly kind: Schema.Literal<"directory">;
|
|
75
|
+
readonly metadata: Schema.Struct<{
|
|
76
|
+
readonly uid: Schema.Finite;
|
|
77
|
+
readonly gid: Schema.Finite;
|
|
78
|
+
readonly mode: Schema.Finite;
|
|
79
|
+
readonly atimeNs: Schema.String;
|
|
80
|
+
readonly mtimeNs: Schema.String;
|
|
81
|
+
readonly ctimeNs: Schema.String;
|
|
82
|
+
readonly birthtimeNs: Schema.String;
|
|
83
|
+
}>;
|
|
84
|
+
readonly entries: Schema.$Array<Schema.Struct<{
|
|
85
|
+
readonly name: Schema.String;
|
|
86
|
+
readonly target: Schema.String;
|
|
87
|
+
}>>;
|
|
88
|
+
}>, Schema.Struct<{
|
|
89
|
+
readonly id: Schema.String;
|
|
90
|
+
readonly kind: Schema.Literal<"file">;
|
|
91
|
+
readonly metadata: Schema.Struct<{
|
|
92
|
+
readonly uid: Schema.Finite;
|
|
93
|
+
readonly gid: Schema.Finite;
|
|
94
|
+
readonly mode: Schema.Finite;
|
|
95
|
+
readonly atimeNs: Schema.String;
|
|
96
|
+
readonly mtimeNs: Schema.String;
|
|
97
|
+
readonly ctimeNs: Schema.String;
|
|
98
|
+
readonly birthtimeNs: Schema.String;
|
|
99
|
+
}>;
|
|
100
|
+
readonly data: Schema.String;
|
|
101
|
+
}>, Schema.Struct<{
|
|
102
|
+
readonly id: Schema.String;
|
|
103
|
+
readonly kind: Schema.Literal<"symlink">;
|
|
104
|
+
readonly metadata: Schema.Struct<{
|
|
105
|
+
readonly uid: Schema.Finite;
|
|
106
|
+
readonly gid: Schema.Finite;
|
|
107
|
+
readonly mode: Schema.Finite;
|
|
108
|
+
readonly atimeNs: Schema.String;
|
|
109
|
+
readonly mtimeNs: Schema.String;
|
|
110
|
+
readonly ctimeNs: Schema.String;
|
|
111
|
+
readonly birthtimeNs: Schema.String;
|
|
112
|
+
}>;
|
|
113
|
+
readonly target: Schema.String;
|
|
114
|
+
}>]>>;
|
|
115
|
+
}>;
|
|
116
|
+
/** @internal */
|
|
117
|
+
export type Document = typeof Document.Type;
|
|
118
|
+
/** @internal */
|
|
119
|
+
export declare const decodedLength: (value: string) => number;
|
|
120
|
+
/** @internal */
|
|
121
|
+
export declare const base64: (input: Uint8Array) => string;
|
|
122
|
+
/** @internal */
|
|
123
|
+
export declare const bytes: (value: string) => Uint8Array;
|
|
124
|
+
/** @internal */
|
|
125
|
+
export declare const inspect: (snapshot: Snapshot) => Effect.Effect<Document, ImageError>;
|
|
126
|
+
/** @internal */
|
|
127
|
+
export declare const capture: (input: unknown, limits?: {
|
|
128
|
+
readonly maxEncodedBytes: number;
|
|
129
|
+
readonly maxRecords: number;
|
|
130
|
+
readonly maxEntries: number;
|
|
131
|
+
readonly maxDecodedBytes: number;
|
|
132
|
+
} | undefined) => Effect.Effect<Snapshot, ImageError, never>;
|
|
133
|
+
/** @internal */
|
|
134
|
+
export declare const encodeSnapshot: (snapshot: Snapshot) => Effect.Effect<Uint8Array<ArrayBuffer>, ImageError, never>;
|
|
135
|
+
/** @internal */
|
|
136
|
+
export declare const decodeSnapshot: (input: Uint8Array<ArrayBufferLike>, limits: {
|
|
137
|
+
readonly maxEncodedBytes: number;
|
|
138
|
+
readonly maxRecords: number;
|
|
139
|
+
readonly maxEntries: number;
|
|
140
|
+
readonly maxDecodedBytes: number;
|
|
141
|
+
}) => Effect.Effect<Snapshot, ImageError, never>;
|
|
142
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../src/internal/image.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAgB,UAAU,EAAE,KAAK,QAAQ,EAAkB,MAAM,gBAAgB,CAAA;AAaxF,gBAAgB;AAChB,eAAO,MAAM,cAAc;;;;;;;;EAQzB,CAAA;AACF,gBAAgB;AAChB,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAC,IAAI,CAAA;AAEvD,gBAAgB;AAChB,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IASjB,CAAA;AACF,gBAAgB;AAChB,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AACvC,gBAAgB;AAChB,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKnB,CAAA;AACF,gBAAgB;AAChB,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAO3C,gBAAgB;AAChB,eAAO,MAAM,aAAa,UAAW,MAAM,KAAG,MACmC,CAAA;AACjF,gBAAgB;AAChB,eAAO,MAAM,MAAM,UAAW,UAAU,KAAG,MAU1C,CAAA;AAGD,gBAAgB;AAChB,eAAO,MAAM,KAAK,UAAW,MAAM,KAAG,UAIrC,CAAA;AACD,gBAAgB;AAChB,eAAO,MAAM,OAAO,aAAc,QAAQ,KAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAI3E,CAAA;AAEJ,gBAAgB;AAChB,eAAO,MAAM,OAAO;;;;;4DAwElB,CAAA;AAEF,gBAAgB;AAChB,eAAO,MAAM,cAAc,mFAKzB,CAAA;AACF,gBAAgB;AAChB,eAAO,MAAM,cAAc;;;;;gDAmB1B,CAAA"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot validation and serialization used by `VirtualFileSystem`.
|
|
3
|
+
*
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Encoding from "effect/Encoding";
|
|
8
|
+
import * as Result from "effect/Result";
|
|
9
|
+
import * as Schema from "effect/Schema";
|
|
10
|
+
import { DecodeLimits, ImageError, SnapshotTypeId } from "../Snapshot.js";
|
|
11
|
+
const natural = Schema.Finite.check(Schema.isInt(), Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER));
|
|
12
|
+
class SnapshotImpl {
|
|
13
|
+
[SnapshotTypeId] = SnapshotTypeId;
|
|
14
|
+
}
|
|
15
|
+
const integer = Schema.String.check(Schema.isPattern(/^(?:0|-?[1-9][0-9]{0,127})(?![\s\S])/));
|
|
16
|
+
/** @internal */
|
|
17
|
+
export const StoredMetadata = Schema.Struct({
|
|
18
|
+
uid: natural,
|
|
19
|
+
gid: natural,
|
|
20
|
+
mode: natural.check(Schema.isLessThanOrEqualTo(0o7777)),
|
|
21
|
+
atimeNs: integer,
|
|
22
|
+
mtimeNs: integer,
|
|
23
|
+
ctimeNs: integer,
|
|
24
|
+
birthtimeNs: integer
|
|
25
|
+
});
|
|
26
|
+
const id = Schema.String.check(Schema.isMinLength(1), Schema.isMaxLength(128));
|
|
27
|
+
/** @internal */
|
|
28
|
+
export const Record = Schema.Union([
|
|
29
|
+
Schema.Struct({
|
|
30
|
+
id,
|
|
31
|
+
kind: Schema.Literal("directory"),
|
|
32
|
+
metadata: StoredMetadata,
|
|
33
|
+
entries: Schema.Array(Schema.Struct({ name: Schema.String, target: id }))
|
|
34
|
+
}),
|
|
35
|
+
Schema.Struct({ id, kind: Schema.Literal("file"), metadata: StoredMetadata, data: Schema.String }),
|
|
36
|
+
Schema.Struct({ id, kind: Schema.Literal("symlink"), metadata: StoredMetadata, target: Schema.String })
|
|
37
|
+
]);
|
|
38
|
+
/** @internal */
|
|
39
|
+
export const Document = Schema.Struct({
|
|
40
|
+
format: Schema.Literal("effect-vfs"),
|
|
41
|
+
version: Schema.Literal(1),
|
|
42
|
+
root: id,
|
|
43
|
+
records: Schema.Array(Record)
|
|
44
|
+
});
|
|
45
|
+
const snapshots = new WeakMap();
|
|
46
|
+
const canonicalBase64Tail = /^(?:[A-Za-z0-9+/]{4}|[A-Za-z0-9+/][AQgw]==|[A-Za-z0-9+/]{2}[AEIMQUYcgkosw048]=)?(?![\s\S])/;
|
|
47
|
+
// Only the final quartet can contain padding. Scanning the alphabet separately avoids
|
|
48
|
+
// the regexp stack growth caused by repeating a four-character group over large files.
|
|
49
|
+
const canonicalBase64 = (value) => value.length % 4 === 0 && !/[^A-Za-z0-9+/]/.test(value.slice(0, -4)) && canonicalBase64Tail.test(value.slice(-4));
|
|
50
|
+
/** @internal */
|
|
51
|
+
export const decodedLength = (value) => value.length / 4 * 3 - (value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0);
|
|
52
|
+
/** @internal */
|
|
53
|
+
export const base64 = (input) => {
|
|
54
|
+
// Effect's encoder concatenates individual characters. Join bounded chunks so snapshots
|
|
55
|
+
// retain flat strings instead of those intermediate string chains. A multiple of three
|
|
56
|
+
// keeps padding in the final chunk only, preserving canonical base64.
|
|
57
|
+
const chunkBytes = 12_288;
|
|
58
|
+
const chunks = [];
|
|
59
|
+
for (let offset = 0; offset < input.length; offset += chunkBytes) {
|
|
60
|
+
chunks.push(Encoding.encodeBase64(input.subarray(offset, offset + chunkBytes)));
|
|
61
|
+
}
|
|
62
|
+
return chunks.join("");
|
|
63
|
+
};
|
|
64
|
+
const error = (code, field) => new ImageError({ code, ...(field === undefined ? {} : { field }) });
|
|
65
|
+
/** @internal */
|
|
66
|
+
export const bytes = (value) => {
|
|
67
|
+
const result = Encoding.decodeBase64(value);
|
|
68
|
+
if (Result.isFailure(result))
|
|
69
|
+
throw new Error("Invalid trusted snapshot base64");
|
|
70
|
+
return result.success;
|
|
71
|
+
};
|
|
72
|
+
/** @internal */
|
|
73
|
+
export const inspect = (snapshot) => Effect.suspend(() => {
|
|
74
|
+
const document = snapshots.get(snapshot);
|
|
75
|
+
return document === undefined ? Effect.fail(error("InvalidStructure", "snapshot")) : Effect.succeed(document);
|
|
76
|
+
});
|
|
77
|
+
/** @internal */
|
|
78
|
+
export const capture = Effect.fnUntraced(function* (input, limits) {
|
|
79
|
+
const decoded = Schema.decodeUnknownResult(Document, { onExcessProperty: "error" })(input);
|
|
80
|
+
if (Result.isFailure(decoded))
|
|
81
|
+
return yield* error("InvalidStructure");
|
|
82
|
+
const document = decoded.success;
|
|
83
|
+
const records = new Map();
|
|
84
|
+
let entries = 0;
|
|
85
|
+
let payload = 0;
|
|
86
|
+
if (limits !== undefined && document.records.length > limits.maxRecords) {
|
|
87
|
+
return yield* error("LimitExceeded", "records");
|
|
88
|
+
}
|
|
89
|
+
for (const record of document.records) {
|
|
90
|
+
if (records.has(record.id))
|
|
91
|
+
return yield* error("InvalidStructure", "id");
|
|
92
|
+
records.set(record.id, record);
|
|
93
|
+
if (record.kind === "directory")
|
|
94
|
+
entries += record.entries.length;
|
|
95
|
+
}
|
|
96
|
+
if (limits !== undefined && entries > limits.maxEntries)
|
|
97
|
+
return yield* error("LimitExceeded", "entries");
|
|
98
|
+
// Count every payload before allocating any decoded payload buffer.
|
|
99
|
+
for (const record of document.records) {
|
|
100
|
+
const values = record.kind === "directory"
|
|
101
|
+
? record.entries.map((entry) => entry.name)
|
|
102
|
+
: [record.kind === "file" ? record.data : record.target];
|
|
103
|
+
for (const value of values) {
|
|
104
|
+
if (!canonicalBase64(value))
|
|
105
|
+
return yield* error("InvalidEncoding", record.id);
|
|
106
|
+
payload += decodedLength(value);
|
|
107
|
+
if (!Number.isSafeInteger(payload) || (limits !== undefined && payload > limits.maxDecodedBytes)) {
|
|
108
|
+
return yield* error("LimitExceeded", "bytes");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const root = records.get(document.root);
|
|
113
|
+
if (root?.kind !== "directory")
|
|
114
|
+
return yield* error("InvalidStructure", "root");
|
|
115
|
+
const parents = new Map();
|
|
116
|
+
for (const record of document.records) {
|
|
117
|
+
if (record.kind === "directory") {
|
|
118
|
+
const names = new Set();
|
|
119
|
+
for (const entry of record.entries) {
|
|
120
|
+
if (names.has(entry.name) || decodedLength(entry.name) < 1 || decodedLength(entry.name) > 255) {
|
|
121
|
+
return yield* error("InvalidStructure", "name");
|
|
122
|
+
}
|
|
123
|
+
names.add(entry.name);
|
|
124
|
+
const name = bytes(entry.name);
|
|
125
|
+
if (name.includes(0) || name.includes(47) || (name.length === 1 && name[0] === 46) ||
|
|
126
|
+
(name.length === 2 && name[0] === 46 && name[1] === 46))
|
|
127
|
+
return yield* error("InvalidStructure", "name");
|
|
128
|
+
const target = records.get(entry.target);
|
|
129
|
+
if (target === undefined)
|
|
130
|
+
return yield* error("InvalidStructure", "target");
|
|
131
|
+
parents.set(target.id, (parents.get(target.id) ?? 0) + 1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (record.kind === "symlink" && bytes(record.target).includes(0)) {
|
|
135
|
+
return yield* error("InvalidStructure", "symlink");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (parents.has(root.id))
|
|
139
|
+
return yield* error("InvalidStructure", "root");
|
|
140
|
+
for (const record of document.records) {
|
|
141
|
+
if (record !== root && (record.kind === "directory" ? parents.get(record.id) !== 1 : !parents.has(record.id))) {
|
|
142
|
+
return yield* error("InvalidStructure", "parent");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const visited = new Set();
|
|
146
|
+
const pending = [root.id];
|
|
147
|
+
while (pending.length > 0) {
|
|
148
|
+
const next = pending.pop();
|
|
149
|
+
if (next === undefined || visited.has(next))
|
|
150
|
+
continue;
|
|
151
|
+
visited.add(next);
|
|
152
|
+
const record = records.get(next);
|
|
153
|
+
if (record?.kind === "directory") {
|
|
154
|
+
for (const entry of record.entries)
|
|
155
|
+
pending.push(entry.target);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (visited.size !== records.size)
|
|
159
|
+
return yield* error("InvalidStructure", "reachability");
|
|
160
|
+
const snapshot = Object.freeze(new SnapshotImpl());
|
|
161
|
+
snapshots.set(snapshot, document);
|
|
162
|
+
return snapshot;
|
|
163
|
+
});
|
|
164
|
+
/** @internal */
|
|
165
|
+
export const encodeSnapshot = Effect.fn("VirtualFileSystem.encodeSnapshot")(function* (snapshot) {
|
|
166
|
+
const text = yield* Schema.encodeEffect(Schema.fromJsonString(Schema.Unknown))(yield* inspect(snapshot)).pipe(Effect.mapError(() => error("InvalidStructure")));
|
|
167
|
+
return new TextEncoder().encode(text);
|
|
168
|
+
});
|
|
169
|
+
/** @internal */
|
|
170
|
+
export const decodeSnapshot = Effect.fn("VirtualFileSystem.decodeSnapshot")(function* (input, limits) {
|
|
171
|
+
const checked = Schema.decodeResult(DecodeLimits, { onExcessProperty: "error" })(limits);
|
|
172
|
+
if (Result.isFailure(checked))
|
|
173
|
+
return yield* error("InvalidStructure", "limits");
|
|
174
|
+
if (!(input instanceof Uint8Array) || !(input.buffer instanceof ArrayBuffer))
|
|
175
|
+
return yield* error("InvalidEncoding");
|
|
176
|
+
if (input.byteLength > checked.success.maxEncodedBytes)
|
|
177
|
+
return yield* error("LimitExceeded", "encodedBytes");
|
|
178
|
+
const text = yield* Effect.try({
|
|
179
|
+
try: () => new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(input)),
|
|
180
|
+
catch: () => error("InvalidEncoding")
|
|
181
|
+
});
|
|
182
|
+
const value = yield* Schema.decodeEffect(Schema.fromJsonString(Schema.Unknown))(text).pipe(Effect.mapError(() => error("InvalidEncoding")));
|
|
183
|
+
if (typeof value === "object" && value !== null && "format" in value && value.format === "effect-vfs" &&
|
|
184
|
+
"version" in value && value.version !== 1)
|
|
185
|
+
return yield* error("UnsupportedVersion");
|
|
186
|
+
return yield* capture(value, checked.success);
|
|
187
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effect-vfs/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A runtime-neutral virtual filesystem core for Effect.",
|
|
5
|
+
"homepage": "https://github.com/lloydrichards/effect-virtual-fs#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/lloydrichards/effect-virtual-fs/issues"
|
|
8
|
+
},
|
|
9
|
+
"keywords": ["effect", "filesystem", "virtual-filesystem", "typescript"],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc --project tsconfig.build.json && tsc --project compatibility/node-next/tsconfig.json",
|
|
18
|
+
"clean": "git clean -xdf .cache .turbo dist node_modules tsconfig.tsbuildinfo",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"type-check": "tsc --noEmit"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"effect": "4.0.0-rc.112"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@repo/config-typescript": "workspace:*",
|
|
27
|
+
"@effect/vitest": "4.0.0-rc.112",
|
|
28
|
+
"effect": "4.0.0-rc.112"
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./VirtualFileSystem": {
|
|
36
|
+
"types": "./dist/VirtualFileSystem.d.ts",
|
|
37
|
+
"import": "./dist/VirtualFileSystem.js"
|
|
38
|
+
},
|
|
39
|
+
"./internal/*": null,
|
|
40
|
+
"./index": null,
|
|
41
|
+
"./*/index": null,
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"files": ["dist/**", "README.md", "LICENSE"],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/lloydrichards/effect-virtual-fs.git",
|
|
51
|
+
"directory": "packages/core"
|
|
52
|
+
}
|
|
53
|
+
}
|