@effect-vfs/core 0.4.0 → 0.6.0

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.
Files changed (143) hide show
  1. package/README.md +39 -32
  2. package/dist/BytePath.d.ts +114 -4
  3. package/dist/BytePath.d.ts.map +1 -1
  4. package/dist/BytePath.js +155 -0
  5. package/dist/Caller.d.ts +391 -0
  6. package/dist/Caller.d.ts.map +1 -0
  7. package/dist/Caller.js +199 -0
  8. package/dist/FileHandle.d.ts +50 -0
  9. package/dist/FileHandle.d.ts.map +1 -0
  10. package/dist/FileHandle.js +28 -0
  11. package/dist/Fixture.d.ts +155 -0
  12. package/dist/Fixture.d.ts.map +1 -0
  13. package/dist/Fixture.js +65 -0
  14. package/dist/LiveVolume.d.ts +15 -22
  15. package/dist/LiveVolume.d.ts.map +1 -1
  16. package/dist/LiveVolume.js +19 -15
  17. package/dist/Metadata.d.ts +186 -0
  18. package/dist/Metadata.d.ts.map +1 -0
  19. package/dist/Metadata.js +125 -0
  20. package/dist/Snapshot.d.ts +31 -53
  21. package/dist/Snapshot.d.ts.map +1 -1
  22. package/dist/Snapshot.js +26 -49
  23. package/dist/SnapshotDelta.d.ts +24 -39
  24. package/dist/SnapshotDelta.d.ts.map +1 -1
  25. package/dist/SnapshotDelta.js +14 -35
  26. package/dist/Target.d.ts +225 -0
  27. package/dist/Target.d.ts.map +1 -0
  28. package/dist/Target.js +92 -0
  29. package/dist/Testing.d.ts +128 -0
  30. package/dist/Testing.d.ts.map +1 -0
  31. package/dist/Testing.js +111 -0
  32. package/dist/VfsError.d.ts +199 -0
  33. package/dist/VfsError.d.ts.map +1 -0
  34. package/dist/VfsError.js +164 -0
  35. package/dist/VirtualFileSystem.d.ts +628 -347
  36. package/dist/VirtualFileSystem.d.ts.map +1 -1
  37. package/dist/VirtualFileSystem.js +450 -117
  38. package/dist/Volume.d.ts +211 -0
  39. package/dist/Volume.d.ts.map +1 -0
  40. package/dist/Volume.js +172 -0
  41. package/dist/Watch.d.ts +68 -0
  42. package/dist/Watch.d.ts.map +1 -0
  43. package/dist/Watch.js +46 -0
  44. package/dist/index.d.ts +56 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +56 -1
  47. package/dist/internal/budget.d.ts +90 -0
  48. package/dist/internal/budget.d.ts.map +1 -0
  49. package/dist/internal/budget.js +70 -0
  50. package/dist/internal/bytePath.d.ts +2 -0
  51. package/dist/internal/bytePath.d.ts.map +1 -1
  52. package/dist/internal/bytePath.js +3 -2
  53. package/dist/internal/bytes.d.ts +3 -0
  54. package/dist/internal/bytes.d.ts.map +1 -1
  55. package/dist/internal/bytes.js +4 -2
  56. package/dist/internal/canonicalBase64.d.ts +1 -4
  57. package/dist/internal/canonicalBase64.d.ts.map +1 -1
  58. package/dist/internal/canonicalBase64.js +9 -13
  59. package/dist/internal/errors.d.ts +54 -0
  60. package/dist/internal/errors.d.ts.map +1 -0
  61. package/dist/internal/errors.js +91 -0
  62. package/dist/internal/{virtualFileSystem/fixture.d.ts → fixture.d.ts} +8 -8
  63. package/dist/internal/fixture.d.ts.map +1 -0
  64. package/dist/internal/fixture.js +133 -0
  65. package/dist/internal/hex128.d.ts +12 -0
  66. package/dist/internal/hex128.d.ts.map +1 -0
  67. package/dist/internal/hex128.js +7 -0
  68. package/dist/internal/hmac.d.ts +5 -0
  69. package/dist/internal/hmac.d.ts.map +1 -0
  70. package/dist/internal/hmac.js +150 -0
  71. package/dist/internal/image.d.ts +14 -117
  72. package/dist/internal/image.d.ts.map +1 -1
  73. package/dist/internal/image.js +137 -167
  74. package/dist/internal/inodeTable.d.ts +20 -0
  75. package/dist/internal/inodeTable.d.ts.map +1 -0
  76. package/dist/internal/inodeTable.js +48 -0
  77. package/dist/internal/lines.d.ts +36 -0
  78. package/dist/internal/lines.d.ts.map +1 -0
  79. package/dist/internal/lines.js +115 -0
  80. package/dist/internal/liveImage.d.ts +19 -278
  81. package/dist/internal/liveImage.d.ts.map +1 -1
  82. package/dist/internal/liveImage.js +75 -166
  83. package/dist/internal/merkle.d.ts +45 -0
  84. package/dist/internal/merkle.d.ts.map +1 -0
  85. package/dist/internal/merkle.js +179 -0
  86. package/dist/internal/metadata.d.ts +2 -13
  87. package/dist/internal/metadata.d.ts.map +1 -1
  88. package/dist/internal/metadata.js +5 -15
  89. package/dist/internal/{virtualFileSystem/overlayDiff.d.ts → overlayDiff.d.ts} +3 -3
  90. package/dist/internal/overlayDiff.d.ts.map +1 -0
  91. package/dist/internal/{virtualFileSystem/overlayDiff.js → overlayDiff.js} +1 -4
  92. package/dist/internal/{virtualFileSystem/path.d.ts → path.d.ts} +19 -7
  93. package/dist/internal/path.d.ts.map +1 -0
  94. package/dist/internal/{virtualFileSystem/path.js → path.js} +39 -25
  95. package/dist/internal/snapshotDelta.d.ts +61 -60
  96. package/dist/internal/snapshotDelta.d.ts.map +1 -1
  97. package/dist/internal/snapshotDelta.js +612 -504
  98. package/dist/internal/snapshotDeltaModel.d.ts.map +1 -1
  99. package/dist/internal/snapshotDeltaModel.js +0 -1
  100. package/dist/internal/stagedState.d.ts +18 -0
  101. package/dist/internal/stagedState.d.ts.map +1 -0
  102. package/dist/internal/stagedState.js +18 -0
  103. package/dist/internal/testSeams.d.ts +17 -0
  104. package/dist/internal/testSeams.d.ts.map +1 -0
  105. package/dist/internal/testSeams.js +20 -0
  106. package/dist/internal/tree.d.ts +225 -0
  107. package/dist/internal/tree.d.ts.map +1 -0
  108. package/dist/internal/tree.js +385 -0
  109. package/dist/internal/turnstile.d.ts +8 -0
  110. package/dist/internal/turnstile.d.ts.map +1 -0
  111. package/dist/internal/turnstile.js +43 -0
  112. package/dist/internal/virtualFileSystem.d.ts +41 -491
  113. package/dist/internal/virtualFileSystem.d.ts.map +1 -1
  114. package/dist/internal/virtualFileSystem.js +2118 -2471
  115. package/dist/internal/volumeState.d.ts +110 -0
  116. package/dist/internal/volumeState.d.ts.map +1 -0
  117. package/dist/internal/volumeState.js +204 -0
  118. package/dist/internal/watchHub.d.ts +25 -0
  119. package/dist/internal/watchHub.d.ts.map +1 -0
  120. package/dist/internal/watchHub.js +68 -0
  121. package/package.json +53 -9
  122. package/dist/VirtualFileSystemError.d.ts +0 -54
  123. package/dist/VirtualFileSystemError.d.ts.map +0 -1
  124. package/dist/VirtualFileSystemError.js +0 -49
  125. package/dist/internal/virtualFileSystem/errors.d.ts +0 -12
  126. package/dist/internal/virtualFileSystem/errors.d.ts.map +0 -1
  127. package/dist/internal/virtualFileSystem/errors.js +0 -47
  128. package/dist/internal/virtualFileSystem/fixture.d.ts.map +0 -1
  129. package/dist/internal/virtualFileSystem/fixture.js +0 -150
  130. package/dist/internal/virtualFileSystem/overlayContent.d.ts +0 -15
  131. package/dist/internal/virtualFileSystem/overlayContent.d.ts.map +0 -1
  132. package/dist/internal/virtualFileSystem/overlayContent.js +0 -29
  133. package/dist/internal/virtualFileSystem/overlayDiff.d.ts.map +0 -1
  134. package/dist/internal/virtualFileSystem/path.d.ts.map +0 -1
  135. package/dist/internal/virtualFileSystem/stagedState.d.ts +0 -22
  136. package/dist/internal/virtualFileSystem/stagedState.d.ts.map +0 -1
  137. package/dist/internal/virtualFileSystem/stagedState.js +0 -62
  138. package/dist/internal/virtualFileSystem/testHooks.d.ts +0 -20
  139. package/dist/internal/virtualFileSystem/testHooks.d.ts.map +0 -1
  140. package/dist/internal/virtualFileSystem/testHooks.js +0 -23
  141. package/dist/internal/virtualFileSystem/watchHub.d.ts +0 -16
  142. package/dist/internal/virtualFileSystem/watchHub.d.ts.map +0 -1
  143. package/dist/internal/virtualFileSystem/watchHub.js +0 -54
@@ -0,0 +1,90 @@
1
+ import * as Schema from "effect/Schema";
2
+ /** @internal */
3
+ export declare const Budget: Schema.Struct<{
4
+ readonly encodedBytes: Schema.ByteSize;
5
+ readonly records: Schema.Natural;
6
+ readonly entries: Schema.Natural;
7
+ readonly decodedBytes: Schema.ByteSize;
8
+ readonly lineBytes: Schema.ByteSize;
9
+ }>;
10
+ /** @internal */
11
+ export type Budget = typeof Budget.Type;
12
+ /** @internal */
13
+ export declare const DeltaBudget: Schema.Struct<{
14
+ readonly encodedBytes: Schema.ByteSize;
15
+ readonly records: Schema.Natural;
16
+ readonly entries: Schema.Natural;
17
+ readonly decodedBytes: Schema.ByteSize;
18
+ readonly identityBytes: Schema.ByteSize;
19
+ readonly baseRecords: Schema.Natural;
20
+ readonly targetRecords: Schema.Natural;
21
+ readonly outputRecords: Schema.Natural;
22
+ readonly outputBytes: Schema.ByteSize;
23
+ readonly inheritedRecords: Schema.Natural;
24
+ }>;
25
+ /** @internal */
26
+ export type DeltaBudget = typeof DeltaBudget.Type;
27
+ /** @internal */
28
+ export declare const BudgetFromDecodeLimits: Schema.decodeTo<Schema.Struct<{
29
+ readonly encodedBytes: Schema.ByteSize;
30
+ readonly records: Schema.Natural;
31
+ readonly entries: Schema.Natural;
32
+ readonly decodedBytes: Schema.ByteSize;
33
+ readonly lineBytes: Schema.ByteSize;
34
+ }>, Schema.Struct<{
35
+ readonly maxEncodedBytes: Schema.ByteSize;
36
+ readonly maxRecords: Schema.Natural;
37
+ readonly maxEntries: Schema.Natural;
38
+ readonly maxDecodedBytes: Schema.ByteSize;
39
+ readonly maxLineBytes: Schema.optionalKey<Schema.ByteSize>;
40
+ }>, never, never>;
41
+ /** @internal */
42
+ export declare const DeltaBudgetFromLimits: Schema.decodeTo<Schema.Struct<{
43
+ readonly encodedBytes: Schema.ByteSize;
44
+ readonly records: Schema.Natural;
45
+ readonly entries: Schema.Natural;
46
+ readonly decodedBytes: Schema.ByteSize;
47
+ readonly identityBytes: Schema.ByteSize;
48
+ readonly baseRecords: Schema.Natural;
49
+ readonly targetRecords: Schema.Natural;
50
+ readonly outputRecords: Schema.Natural;
51
+ readonly outputBytes: Schema.ByteSize;
52
+ readonly inheritedRecords: Schema.Natural;
53
+ }>, Schema.Struct<{
54
+ readonly maxEncodedBytes: Schema.ByteSize;
55
+ readonly maxIdentityBytes: Schema.ByteSize;
56
+ readonly maxDeltaRecords: Schema.Natural;
57
+ readonly maxDecodedDeltaBytes: Schema.ByteSize;
58
+ readonly maxBaseRecords: Schema.Natural;
59
+ readonly maxTargetRecords: Schema.Natural;
60
+ readonly maxEntries: Schema.Natural;
61
+ readonly maxOutputRecords: Schema.Natural;
62
+ readonly maxOutputBytes: Schema.ByteSize;
63
+ readonly maxInheritedRecords: Schema.Natural;
64
+ }> & {
65
+ constrained: {
66
+ readonly maxEncodedBytes: import("effect/ByteSize").ByteSize;
67
+ readonly maxIdentityBytes: import("effect/ByteSize").ByteSize;
68
+ readonly maxDeltaRecords: number;
69
+ readonly maxDecodedDeltaBytes: import("effect/ByteSize").ByteSize;
70
+ readonly maxBaseRecords: number;
71
+ readonly maxTargetRecords: number;
72
+ readonly maxEntries: number;
73
+ readonly maxOutputRecords: number;
74
+ readonly maxOutputBytes: import("effect/ByteSize").ByteSize;
75
+ readonly maxInheritedRecords: number;
76
+ };
77
+ default: {
78
+ readonly maxEncodedBytes: import("effect/ByteSize").ByteSize;
79
+ readonly maxIdentityBytes: import("effect/ByteSize").ByteSize;
80
+ readonly maxDeltaRecords: number;
81
+ readonly maxDecodedDeltaBytes: import("effect/ByteSize").ByteSize;
82
+ readonly maxBaseRecords: number;
83
+ readonly maxTargetRecords: number;
84
+ readonly maxEntries: number;
85
+ readonly maxOutputRecords: number;
86
+ readonly maxOutputBytes: import("effect/ByteSize").ByteSize;
87
+ readonly maxInheritedRecords: number;
88
+ };
89
+ }, never, never>;
90
+ //# sourceMappingURL=budget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/internal/budget.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAYvC,gBAAgB;AAChB,eAAO,MAAM,MAAM;;;;;;EAGjB,CAAA;AAEF,gBAAgB;AAChB,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;AAEvC,gBAAgB;AAChB,eAAO,MAAM,WAAW;;;;;;;;;;;EAQtB,CAAA;AAEF,gBAAgB;AAChB,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AAEjD,gBAAgB;AAChB,eAAO,MAAM,sBAAsB;;;;;;;;;;;;iBAmBjC,CAAA;AAEF,gBAAgB;AAChB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBA4BhC,CAAA"}
@@ -0,0 +1,70 @@
1
+ import * as Schema from "effect/Schema";
2
+ import * as SchemaTransformation from "effect/SchemaTransformation";
3
+ import { DecodeLimits } from "../Snapshot.js";
4
+ import { SnapshotDeltaLimits } from "../SnapshotDelta.js";
5
+ const documentFields = {
6
+ encodedBytes: Schema.ByteSize,
7
+ records: Schema.Natural,
8
+ entries: Schema.Natural,
9
+ decodedBytes: Schema.ByteSize
10
+ };
11
+ /** @internal */
12
+ export const Budget = Schema.Struct({
13
+ ...documentFields,
14
+ lineBytes: Schema.ByteSize
15
+ });
16
+ /** @internal */
17
+ export const DeltaBudget = Schema.Struct({
18
+ ...documentFields,
19
+ identityBytes: Schema.ByteSize,
20
+ baseRecords: Schema.Natural,
21
+ targetRecords: Schema.Natural,
22
+ outputRecords: Schema.Natural,
23
+ outputBytes: Schema.ByteSize,
24
+ inheritedRecords: Schema.Natural
25
+ });
26
+ /** @internal */
27
+ export const BudgetFromDecodeLimits = DecodeLimits.pipe(Schema.decodeTo(Budget, SchemaTransformation.transform({
28
+ decode: (limits) => ({
29
+ encodedBytes: limits.maxEncodedBytes,
30
+ records: limits.maxRecords,
31
+ entries: limits.maxEntries,
32
+ decodedBytes: limits.maxDecodedBytes,
33
+ // The encoded input bounds any line when maxLineBytes is absent.
34
+ lineBytes: limits.maxLineBytes ?? limits.maxEncodedBytes
35
+ }),
36
+ encode: (budget) => ({
37
+ maxEncodedBytes: budget.encodedBytes,
38
+ maxRecords: budget.records,
39
+ maxEntries: budget.entries,
40
+ maxDecodedBytes: budget.decodedBytes,
41
+ maxLineBytes: budget.lineBytes
42
+ })
43
+ })));
44
+ /** @internal */
45
+ export const DeltaBudgetFromLimits = SnapshotDeltaLimits.pipe(Schema.decodeTo(DeltaBudget, SchemaTransformation.transform({
46
+ decode: (limits) => ({
47
+ encodedBytes: limits.maxEncodedBytes,
48
+ records: limits.maxDeltaRecords,
49
+ entries: limits.maxEntries,
50
+ decodedBytes: limits.maxDecodedDeltaBytes,
51
+ identityBytes: limits.maxIdentityBytes,
52
+ baseRecords: limits.maxBaseRecords,
53
+ targetRecords: limits.maxTargetRecords,
54
+ outputRecords: limits.maxOutputRecords,
55
+ outputBytes: limits.maxOutputBytes,
56
+ inheritedRecords: limits.maxInheritedRecords
57
+ }),
58
+ encode: (budget) => ({
59
+ maxEncodedBytes: budget.encodedBytes,
60
+ maxIdentityBytes: budget.identityBytes,
61
+ maxDeltaRecords: budget.records,
62
+ maxDecodedDeltaBytes: budget.decodedBytes,
63
+ maxBaseRecords: budget.baseRecords,
64
+ maxTargetRecords: budget.targetRecords,
65
+ maxEntries: budget.entries,
66
+ maxOutputRecords: budget.outputRecords,
67
+ maxOutputBytes: budget.outputBytes,
68
+ maxInheritedRecords: budget.inheritedRecords
69
+ })
70
+ })));
@@ -4,6 +4,8 @@ export declare const BytePathId: "@effect-vfs/core/BytePath";
4
4
  /** @internal */
5
5
  export declare const isBytePath: (value: unknown) => value is BytePath;
6
6
  /** @internal */
7
+ export declare const isPathBytes: (bytes: Uint8Array) => boolean;
8
+ /** @internal */
7
9
  export declare const make: (bytes: Uint8Array) => BytePath;
8
10
  /** @internal */
9
11
  export declare const getBytes: (path: BytePath) => Uint8Array | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"bytePath.d.ts","sourceRoot":"","sources":["../../src/internal/bytePath.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAG9C,gBAAgB;AAChB,eAAO,MAAM,UAAU,EAAG,2BAAoC,CAAA;AA6B9D,gBAAgB;AAChB,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,KAAK,IAAI,QAA6D,CAAA;AAElH,gBAAgB;AAChB,eAAO,MAAM,IAAI,UAAW,UAAU,KAAG,QAMxC,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,QAAQ,SAAU,QAAQ,KAAG,UAAU,GAAG,SAAgC,CAAA"}
1
+ {"version":3,"file":"bytePath.d.ts","sourceRoot":"","sources":["../../src/internal/bytePath.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAG9C,gBAAgB;AAChB,eAAO,MAAM,UAAU,EAAG,2BAAoC,CAAA;AA6B9D,gBAAgB;AAChB,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,KAAK,IAAI,QAA6D,CAAA;AAElH,gBAAgB;AAChB,eAAO,MAAM,WAAW,UAAW,UAAU,KAAG,OAAiD,CAAA;AAEjG,gBAAgB;AAChB,eAAO,MAAM,IAAI,UAAW,UAAU,KAAG,QAMxC,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,QAAQ,SAAU,QAAQ,KAAG,UAAU,GAAG,SAAgC,CAAA"}
@@ -1,4 +1,3 @@
1
- // Opaque byte path representation and value protocols.
2
1
  import * as Equal from "effect/Equal";
3
2
  import * as Hash from "effect/Hash";
4
3
  import { pipeArguments } from "effect/Pipeable";
@@ -31,8 +30,10 @@ const BytePathProto = {
31
30
  /** @internal */
32
31
  export const isBytePath = (value) => Predicate.isObject(value) && bytePaths.has(value);
33
32
  /** @internal */
33
+ export const isPathBytes = (bytes) => bytes.length > 0 && !bytes.includes(0);
34
+ /** @internal */
34
35
  export const make = (bytes) => {
35
- // SAFETY: BytePathProto implements every member of the opaque BytePath contract.
36
+ // SAFETY: BytePathProto implements the opaque BytePath contract.
36
37
  const path = Object.create(BytePathProto);
37
38
  bytePaths.set(path, bytes);
38
39
  return Object.freeze(path);
@@ -1,6 +1,9 @@
1
+ import * as Effect from "effect/Effect";
1
2
  import * as Order from "effect/Order";
2
3
  /** @internal */
3
4
  export declare const bytesOrder: Order.Order<Uint8Array>;
4
5
  /** @internal */
5
6
  export declare const sameBytes: (left: Uint8Array | undefined, right: Uint8Array | undefined) => boolean;
7
+ /** @internal */
8
+ export declare const decodeUtf8: <E>(bytes: Uint8Array, onInvalid: (cause: unknown) => E) => Effect.Effect<string, E>;
6
9
  //# sourceMappingURL=bytes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/internal/bytes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAerC,gBAAgB;AAChB,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAA4B,CAAA;AAG3E,gBAAgB;AAChB,eAAO,MAAM,SAAS,SAAU,UAAU,GAAG,SAAS,SAAS,UAAU,GAAG,SAAS,KAAG,OACU,CAAA"}
1
+ {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/internal/bytes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAerC,gBAAgB;AAChB,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,UAAU,CAA4B,CAAA;AAE3E,gBAAgB;AAChB,eAAO,MAAM,SAAS,SAAU,UAAU,GAAG,SAAS,SAAS,UAAU,GAAG,SAAS,KAAG,OACU,CAAA;AAGlG,gBAAgB;AAChB,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,UAAU,aAAa,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CACW,CAAA"}
@@ -1,4 +1,4 @@
1
- // Byte-array comparison shared by the overlay diff, the snapshot delta, and BytePath equality.
1
+ import * as Effect from "effect/Effect";
2
2
  import * as Order from "effect/Order";
3
3
  const compareBytes = (left, right) => {
4
4
  const length = Math.min(left.length, right.length);
@@ -12,6 +12,8 @@ const compareBytes = (left, right) => {
12
12
  };
13
13
  /** @internal */
14
14
  export const bytesOrder = Order.make(compareBytes);
15
- // A plain loop: Equal.equals would hash both arrays and memoise the pair in a WeakMap first.
16
15
  /** @internal */
17
16
  export const sameBytes = (left, right) => left === right || (left !== undefined && right !== undefined && compareBytes(left, right) === 0);
17
+ // Copy the input to prevent a shared buffer changing during decoding.
18
+ /** @internal */
19
+ export const decodeUtf8 = (bytes, onInvalid) => Effect.try({ try: () => new TextDecoder("utf-8", { fatal: true }).decode(new Uint8Array(bytes)), catch: onInvalid });
@@ -1,15 +1,12 @@
1
- import * as Effect from "effect/Effect";
2
1
  import * as Schema from "effect/Schema";
3
2
  declare const Encoded: Schema.brand<Schema.refine<string, Schema.String>, "CanonicalBase64">;
4
3
  type Encoded = typeof Encoded.Type;
5
4
  /** @internal */
6
5
  export declare const CanonicalBase64: {
7
6
  Encoded: Schema.brand<Schema.refine<string, Schema.String>, "CanonicalBase64">;
8
- Bytes: Schema.decodeTo<Schema.Uint8Array, Schema.brand<Schema.refine<string, Schema.String>, "CanonicalBase64">, never, never>;
9
- decode: (input: Encoded) => Effect.Effect<Uint8Array<ArrayBufferLike>, never, never>;
7
+ toBytes: (input: Encoded) => Uint8Array;
10
8
  encode: (input: Uint8Array) => Encoded;
11
9
  decodedLength: (value: Encoded) => number;
12
- is: (value: string) => value is Encoded;
13
10
  };
14
11
  export {};
15
12
  //# sourceMappingURL=canonicalBase64.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"canonicalBase64.d.ts","sourceRoot":"","sources":["../../src/internal/canonicalBase64.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AA0BvC,QAAA,MAAM,OAAO,uEAGZ,CAAA;AASD,KAAK,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAElC,gBAAgB;AAChB,eAAO,MAAM,eAAe;;;oBAGV,OAAO;oBACP,UAAU,KAAG,OAAO;2BACb,OAAO,KAAG,MAAM;gBAC3B,MAAM,KAAG,KAAK,IAAI,OAAO;CACtC,CAAA"}
1
+ {"version":3,"file":"canonicalBase64.d.ts","sourceRoot":"","sources":["../../src/internal/canonicalBase64.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAyBvC,QAAA,MAAM,OAAO,uEAMZ,CAAA;AAED,KAAK,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AAElC,gBAAgB;AAChB,eAAO,MAAM,eAAe;;qBAET,OAAO,KAAG,UAAU;oBACrB,UAAU,KAAG,OAAO;2BACb,OAAO,KAAG,MAAM;CACxC,CAAA"}
@@ -1,15 +1,14 @@
1
- import * as Effect from "effect/Effect";
2
1
  import * as Encoding from "effect/Encoding";
2
+ import * as Result from "effect/Result";
3
3
  import * as Schema from "effect/Schema";
4
- import * as SchemaGetter from "effect/SchemaGetter";
4
+ import { ENCODING_CHECK } from "./errors.js";
5
5
  const canonicalTail = /^(?:[A-Za-z0-9+/]{4}|[A-Za-z0-9+/][AQgw]==|[A-Za-z0-9+/]{2}[AEIMQUYcgkosw048]=)?(?![\s\S])/;
6
6
  const isCanonical = (value) => value.length % 4 === 0 &&
7
7
  !/[^A-Za-z0-9+/]/.test(value.slice(0, -4)) &&
8
8
  canonicalTail.test(value.slice(-4));
9
9
  const decodedLength = (value) => value.length / 4 * 3 - (value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0);
10
10
  const encode = (input) => {
11
- // Join bounded chunks to avoid the encoder's intermediate string chains. Three-byte
12
- // boundaries keep padding in the final chunk, preserving the canonical representation.
11
+ // Chunk boundaries must be multiples of three to keep padding in the final chunk.
13
12
  const chunkBytes = 12_288;
14
13
  const chunks = [];
15
14
  for (let offset = 0; offset < input.length; offset += chunkBytes) {
@@ -17,17 +16,14 @@ const encode = (input) => {
17
16
  }
18
17
  return Encoded.make(chunks.join(""));
19
18
  };
20
- const Encoded = Schema.String.pipe(Schema.refine((value) => isCanonical(value), { expected: "canonical padded base64" }), Schema.brand("CanonicalBase64"));
21
- const Bytes = Encoded.pipe(Schema.decodeTo(Schema.Uint8Array, {
22
- decode: SchemaGetter.decodeBase64(),
23
- encode: SchemaGetter.transform((input) => encode(input))
24
- }));
19
+ const Encoded = Schema.String.pipe(Schema.refine((value) => isCanonical(value), {
20
+ expected: "canonical padded base64",
21
+ [ENCODING_CHECK]: true
22
+ }), Schema.brand("CanonicalBase64"));
25
23
  /** @internal */
26
24
  export const CanonicalBase64 = {
27
25
  Encoded,
28
- Bytes,
29
- decode: (input) => Schema.decodeEffect(Bytes)(input).pipe(Effect.orDie),
26
+ toBytes: (input) => Result.getOrThrow(Encoding.decodeBase64(input)),
30
27
  encode: (input) => encode(input),
31
- decodedLength: (value) => decodedLength(value),
32
- is: (value) => isCanonical(value)
28
+ decodedLength: (value) => decodedLength(value)
33
29
  };
@@ -0,0 +1,54 @@
1
+ import * as Result from "effect/Result";
2
+ import * as Schema from "effect/Schema";
3
+ import type * as SchemaAST from "effect/SchemaAST";
4
+ import type * as SchemaIssue from "effect/SchemaIssue";
5
+ import type { BytePath } from "../BytePath.js";
6
+ import { type ArgumentFailure, type FsCode, type FsFailure, type ImageCode, type ImageFailure, VfsError } from "../VfsError.js";
7
+ import type { PathInput } from "../VirtualFileSystem.js";
8
+ /** @internal */
9
+ export { VfsError };
10
+ /** @internal */
11
+ export declare const errorPath: (input: PathInput) => BytePath | undefined;
12
+ interface Details {
13
+ readonly path?: PathInput;
14
+ readonly field?: string;
15
+ readonly cause?: unknown;
16
+ }
17
+ /** @internal */
18
+ export declare const argumentFailure: (operation: string, field: string, cause?: unknown) => ArgumentFailure;
19
+ /** @internal */
20
+ export declare const decodeConfiguration: <A, I>(schema: Schema.Codec<A, I>, value: typeof Schema.Unknown.Type, operation: string) => Result.Result<A, ArgumentFailure>;
21
+ /** @internal */
22
+ export declare const ENCODING_CHECK = "@effect-vfs/core/encodingCheck";
23
+ /** @internal */
24
+ export interface IssueSite {
25
+ readonly path: ReadonlyArray<PropertyKey>;
26
+ readonly checks: ReadonlyArray<SchemaAST.Filter<unknown>>;
27
+ }
28
+ /** @internal */
29
+ export declare const issueSite: (issue: SchemaIssue.Issue) => IssueSite;
30
+ /** @internal */
31
+ export declare const isEncodingIssue: (site: IssueSite) => boolean;
32
+ /** @internal */
33
+ export declare const fsFailure: (code: FsCode, operation: string, details?: Details) => FsFailure;
34
+ /** @internal */
35
+ export declare const imageFailure: (operation: string, code: ImageCode, details?: {
36
+ readonly field?: string;
37
+ readonly cause?: unknown;
38
+ }) => ImageFailure;
39
+ /** @internal */
40
+ export declare const retargetFailure: <E>(operation: string, error: E) => E;
41
+ /** @internal */
42
+ export interface OpContext {
43
+ readonly operation: string;
44
+ readonly fail: (code: FsCode, details?: {
45
+ readonly cause?: unknown;
46
+ readonly field?: string;
47
+ }) => FsFailure;
48
+ readonly at: (path: PathInput) => OpContext;
49
+ }
50
+ /** @internal */
51
+ export declare const OpContext: {
52
+ make: (operation: string) => OpContext;
53
+ };
54
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/internal/errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,KAAK,SAAS,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,KAAK,WAAW,MAAM,oBAAoB,CAAA;AACtD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,MAAM,EACX,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,YAAY,EAGjB,QAAQ,EACT,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAGxD,gBAAgB;AAChB,OAAO,EAAE,QAAQ,EAAE,CAAA;AAKnB,gBAAgB;AAChB,eAAO,MAAM,SAAS,UAAW,SAAS,KAAG,QAAQ,GAAG,SAMvD,CAAA;AAYD,UAAU,OAAO;IACf,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CACzB;AAWD,gBAAgB;AAChB,eAAO,MAAM,eAAe,cAAe,MAAM,SAAS,MAAM,UAAU,OAAO,KAAG,eAC7B,CAAA;AAEvD,gBAAgB;AAChB,eAAO,MAAM,mBAAmB,GAAI,CAAC,EAAE,CAAC,UAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,SACnB,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,aACtB,MAAM,KAChB,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,eAAe,CAGhC,CAAA;AAGH,gBAAgB;AAChB,eAAO,MAAM,cAAc,mCAAmC,CAAA;AAE9D,gBAAgB;AAChB,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;IACzC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA;CAC1D;AAGD,gBAAgB;AAChB,eAAO,MAAM,SAAS,UAAW,WAAW,CAAC,KAAK,KAAG,SAmBpD,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,eAAe,SAAU,SAAS,KAAG,OACyB,CAAA;AAE3E,gBAAgB;AAChB,eAAO,MAAM,SAAS,SAAU,MAAM,aAAa,MAAM,YAAY,OAAO,KAAG,SAC9C,CAAA;AAEjC,gBAAgB;AAChB,eAAO,MAAM,YAAY,cACZ,MAAM,QACX,SAAS,YACL;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KAC9D,YAA+C,CAAA;AAQlD,gBAAgB;AAChB,eAAO,MAAM,eAAe,GAAI,CAAC,aAAa,MAAM,SAAS,CAAC,KAAG,CAMhE,CAAA;AAED,gBAAgB;AAChB,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAA;IAC3G,QAAQ,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,SAAS,KAAK,SAAS,CAAA;CAC5C;AAQD,gBAAgB;AAChB,eAAO,MAAM,SAAS;IACpB,IAAI,cAAc,MAAM,KAAG,SAAS;CACrC,CAAA"}
@@ -0,0 +1,91 @@
1
+ import * as Predicate from "effect/Predicate";
2
+ import * as Result from "effect/Result";
3
+ import * as Schema from "effect/Schema";
4
+ import { make as makeError, VfsError } from "../VfsError.js";
5
+ import { getBytes, isBytePath, isPathBytes, make } from "./bytePath.js";
6
+ /** @internal */
7
+ export { VfsError };
8
+ const encoder = new TextEncoder();
9
+ // Omit invalid paths rather than attach a BytePath that breaks its invariant.
10
+ /** @internal */
11
+ export const errorPath = (input) => {
12
+ const bytes = Predicate.isString(input) ? encoder.encode(input) : isBytePath(input) ? getBytes(input) : undefined;
13
+ if (bytes === undefined || !isPathBytes(bytes))
14
+ return undefined;
15
+ return isBytePath(input) ? input : make(bytes);
16
+ };
17
+ const configurationField = (issue) => {
18
+ if (Predicate.isTagged("Pointer")(issue))
19
+ return issue.path.map(String).join(".");
20
+ if (Predicate.isTagged("Composite")(issue))
21
+ return configurationField(issue.issues[0]);
22
+ if (Predicate.isTagged("Encoding")(issue))
23
+ return configurationField(issue.issue);
24
+ return "options";
25
+ };
26
+ const build = (code, operation, details) => makeError({
27
+ code,
28
+ operation,
29
+ field: details?.field,
30
+ path: details?.path === undefined ? undefined : errorPath(details.path),
31
+ cause: details?.cause
32
+ });
33
+ /** @internal */
34
+ export const argumentFailure = (operation, field, cause) => build("InvalidArgument", operation, { field, cause });
35
+ /** @internal */
36
+ export const decodeConfiguration = (schema, value, operation) => Schema.decodeUnknownResult(schema, { onExcessProperty: "error" })(value).pipe(Result.mapError((error) => argumentFailure(operation, configurationField(error.issue), error)));
37
+ // Distinguishes invalid value encoding from invalid structure.
38
+ /** @internal */
39
+ export const ENCODING_CHECK = "@effect-vfs/core/encodingCheck";
40
+ // Decoding stops at the first failure; composite issues put it in their first branch.
41
+ /** @internal */
42
+ export const issueSite = (issue) => {
43
+ const path = [];
44
+ const checks = [];
45
+ let current = issue;
46
+ while (current !== undefined) {
47
+ if (Predicate.isTagged("Pointer")(current)) {
48
+ path.push(...current.path);
49
+ current = current.issue;
50
+ }
51
+ else if (Predicate.isTagged("Filter")(current)) {
52
+ checks.push(current.filter);
53
+ current = current.issue;
54
+ }
55
+ else if (Predicate.isTagged("Encoding")(current))
56
+ current = current.issue;
57
+ else if (Predicate.isTagged("Composite")(current) || Predicate.isTagged("AnyOf")(current)) {
58
+ current = current.issues[0];
59
+ }
60
+ else
61
+ current = undefined;
62
+ }
63
+ return { path, checks };
64
+ };
65
+ /** @internal */
66
+ export const isEncodingIssue = (site) => site.checks.some((check) => check.annotations?.[ENCODING_CHECK] === true);
67
+ /** @internal */
68
+ export const fsFailure = (code, operation, details) => build(code, operation, details);
69
+ /** @internal */
70
+ export const imageFailure = (operation, code, details) => build(code, operation, details);
71
+ /** @internal */
72
+ const retarget = (operation, error) => error.operation === operation
73
+ ? error
74
+ : makeError({ code: error.code, operation, field: error.field, path: error.path, cause: error.cause });
75
+ /** @internal */
76
+ export const retargetFailure = (operation, error) => {
77
+ if (!Schema.is(VfsError)(error))
78
+ return error;
79
+ // SAFETY: retarget changes only the operation, preserving E's error variant.
80
+ // oxlint-disable-next-line effecttsgo/unsafe-effect-type-assertion -- see the invariant above.
81
+ return retarget(operation, error);
82
+ };
83
+ const makeOpContext = (operation, located) => ({
84
+ operation,
85
+ fail: (code, details) => fsFailure(code, operation, located === undefined ? details : { ...details, path: located }),
86
+ at: (path) => makeOpContext(operation, Predicate.isString(path) || isBytePath(path) ? path : undefined)
87
+ });
88
+ /** @internal */
89
+ export const OpContext = {
90
+ make: (operation) => makeOpContext(operation)
91
+ };
@@ -1,5 +1,5 @@
1
1
  import * as Effect from "effect/Effect";
2
- import { ImageError } from "../../Snapshot.js";
2
+ import { VfsError } from "./errors.js";
3
3
  /** @internal */
4
4
  export declare const fromFixture: (fixture: {
5
5
  readonly rootMetadata?: {
@@ -13,7 +13,7 @@ export declare const fromFixture: (fixture: {
13
13
  };
14
14
  readonly entries: readonly ({
15
15
  readonly kind: "directory";
16
- readonly path: string | import("../../BytePath.js").BytePath;
16
+ readonly path: string | import("../BytePath.js").BytePath;
17
17
  readonly metadata?: {
18
18
  readonly uid?: number;
19
19
  readonly gid?: number;
@@ -25,7 +25,7 @@ export declare const fromFixture: (fixture: {
25
25
  };
26
26
  } | {
27
27
  readonly kind: "file";
28
- readonly path: string | import("../../BytePath.js").BytePath;
28
+ readonly path: string | import("../BytePath.js").BytePath;
29
29
  readonly bytes: Uint8Array<ArrayBufferLike>;
30
30
  readonly metadata?: {
31
31
  readonly uid?: number;
@@ -38,8 +38,8 @@ export declare const fromFixture: (fixture: {
38
38
  };
39
39
  } | {
40
40
  readonly kind: "symlink";
41
- readonly path: string | import("../../BytePath.js").BytePath;
42
- readonly target: string | import("../../BytePath.js").BytePath;
41
+ readonly path: string | import("../BytePath.js").BytePath;
42
+ readonly target: string | import("../BytePath.js").BytePath;
43
43
  readonly metadata?: {
44
44
  readonly uid?: number;
45
45
  readonly gid?: number;
@@ -51,8 +51,8 @@ export declare const fromFixture: (fixture: {
51
51
  };
52
52
  } | {
53
53
  readonly kind: "hardLink";
54
- readonly path: string | import("../../BytePath.js").BytePath;
55
- readonly target: string | import("../../BytePath.js").BytePath;
54
+ readonly path: string | import("../BytePath.js").BytePath;
55
+ readonly target: string | import("../BytePath.js").BytePath;
56
56
  })[];
57
57
  }, options?: {
58
58
  readonly identity?: string & import("effect/Brand").Brand<"@effect-vfs/core/VolumeIdentity">;
@@ -62,5 +62,5 @@ export declare const fromFixture: (fixture: {
62
62
  readonly maxBytes?: import("effect/ByteSize").ByteSize;
63
63
  readonly maxFileBytes?: import("effect/ByteSize").ByteSize;
64
64
  readonly maxPathBytes?: import("effect/ByteSize").ByteSize;
65
- } | undefined) => Effect.Effect<import("../../VirtualFileSystem.js").Volume, import("./errors.js").ConfigurationError | ImageError | import("effect/PlatformError").PlatformError, import("effect/Crypto").Crypto>;
65
+ } | undefined) => Effect.Effect<import("../VirtualFileSystem.js").Volume, import("../VfsError.js").ArgumentFailure | import("../VfsError.js").ImageFailure | (void & VfsError), never>;
66
66
  //# sourceMappingURL=fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/internal/fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAMvC,OAAO,EAAgD,QAAQ,EAAE,MAAM,aAAa,CAAA;AAgBpF,gBAAgB;AAChB,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sLAiLvB,CAAA"}
@@ -0,0 +1,133 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Result from "effect/Result";
3
+ import * as Schema from "effect/Schema";
4
+ import { Fixture as FixtureSchema, FixtureEntry } from "../Fixture.js";
5
+ import { VolumeIdentity, VolumeOptions as VolumeOptionsSchema } from "../Volume.js";
6
+ import { decodeConfiguration, imageFailure, OpContext, VfsError } from "./errors.js";
7
+ import { inputBytes, isAttachedBytes, isDotComponent, preparePath } from "./path.js";
8
+ import { makeVolume, VolumeSource } from "./virtualFileSystem.js";
9
+ import { assemble, Ino, ROOT_INO } from "./volumeState.js";
10
+ const DEFAULT_MODE = { directory: 0o755, file: 0o644, symlink: 0o777 };
11
+ const EPOCH_NS = 0n;
12
+ /** @internal */
13
+ export const fromFixture = Effect.fn("VirtualFileSystem.fromFixture")(function* (fixture, options) {
14
+ const config = yield* Effect.fromResult(decodeConfiguration(VolumeOptionsSchema, options ?? {}, "fromFixture"));
15
+ const source = yield* Schema.decodeEffect(FixtureSchema, { onExcessProperty: "error" })(fixture).pipe(Effect.mapError((cause) => imageFailure("fromFixture", "InvalidStructure", { field: "fixture", cause })));
16
+ if (source.entries.some((entry) => FixtureEntry.match(entry, {
17
+ directory: () => false,
18
+ file: ({ bytes }) => !isAttachedBytes(bytes),
19
+ symlink: () => false,
20
+ hardLink: () => false
21
+ }))) {
22
+ return yield* imageFailure("fromFixture", "InvalidEncoding", { field: "bytes" });
23
+ }
24
+ const metadata = (kind, overrides) => ({
25
+ uid: overrides?.uid ?? 0,
26
+ gid: overrides?.gid ?? 0,
27
+ mode: overrides?.mode ?? DEFAULT_MODE[kind],
28
+ atimeNs: overrides?.atimeNs ?? EPOCH_NS,
29
+ mtimeNs: overrides?.mtimeNs ?? EPOCH_NS,
30
+ ctimeNs: overrides?.ctimeNs ?? EPOCH_NS,
31
+ birthtimeNs: overrides?.birthtimeNs ?? EPOCH_NS
32
+ });
33
+ // Assign inode numbers in declaration order, before resolving hard links.
34
+ const declarations = new Map();
35
+ const aliases = new Map();
36
+ const paths = new Map();
37
+ let nextInode = ROOT_INO;
38
+ const declare = (declared) => {
39
+ nextInode = Ino(nextInode + 1);
40
+ return { ...declared, ino: nextInode };
41
+ };
42
+ declarations.set("", { kind: "directory", ino: ROOT_INO, metadata: metadata("directory", source.rootMetadata) });
43
+ const op = OpContext.make("fixture");
44
+ const fixturePath = (input) => preparePath(input, op.operation, config.maxPathBytes).pipe(Result.flatMap((path) => !path.absolute || path.components.length === 0 ||
45
+ path.components.some(isDotComponent)
46
+ ? Result.fail(op.at(input).fail("InvalidArgument")) :
47
+ Result.succeed(path.components)));
48
+ // Copy caller-owned bytes before storing them.
49
+ for (const entry of source.entries) {
50
+ const components = yield* Effect.fromResult(fixturePath(entry.path)).pipe(Effect.mapError((cause) => imageFailure("fromFixture", "InvalidStructure", { field: "path", cause })));
51
+ const key = components.join("/");
52
+ if (paths.has(key))
53
+ return yield* imageFailure("fromFixture", "InvalidStructure", { field: "duplicate" });
54
+ paths.set(key, components);
55
+ const error = FixtureEntry.match(entry, {
56
+ hardLink: ({ target }) => {
57
+ const parsedTarget = fixturePath(target);
58
+ if (Result.isFailure(parsedTarget)) {
59
+ return imageFailure("fromFixture", "InvalidStructure", { field: "target", cause: parsedTarget.failure });
60
+ }
61
+ aliases.set(key, parsedTarget.success.join("/"));
62
+ },
63
+ directory: (entry) => {
64
+ declarations.set(key, declare({ kind: "directory", metadata: metadata("directory", entry.metadata) }));
65
+ },
66
+ file: (entry) => {
67
+ declarations.set(key, declare({ kind: "file", metadata: metadata("file", entry.metadata), payload: entry.bytes.slice() }));
68
+ },
69
+ symlink: ({ metadata: overrides, target: input }) => {
70
+ const target = inputBytes(input);
71
+ if (Result.isFailure(target)) {
72
+ return imageFailure("fromFixture", target.failure === "InvalidPathEncoding" ? "InvalidEncoding" : "InvalidStructure", { field: "target" });
73
+ }
74
+ if (target.success.includes(0))
75
+ return imageFailure("fromFixture", "InvalidStructure", { field: "target" });
76
+ declarations.set(key, declare({ kind: "symlink", metadata: metadata("symlink", overrides), payload: target.success.slice() }));
77
+ }
78
+ });
79
+ if (Schema.is(VfsError)(error))
80
+ return yield* error;
81
+ }
82
+ for (const [key] of aliases) {
83
+ let target = key;
84
+ const seen = new Set();
85
+ while (!declarations.has(target)) {
86
+ if (seen.has(target))
87
+ return yield* imageFailure("fromFixture", "InvalidStructure", { field: "hardLink" });
88
+ seen.add(target);
89
+ const next = aliases.get(target);
90
+ if (next === undefined)
91
+ return yield* imageFailure("fromFixture", "InvalidStructure", { field: "hardLink" });
92
+ target = next;
93
+ }
94
+ const node = declarations.get(target);
95
+ if (node === undefined || node.kind === "directory") {
96
+ return yield* imageFailure("fromFixture", "InvalidStructure", { field: "hardLink" });
97
+ }
98
+ for (const alias of seen)
99
+ declarations.set(alias, node);
100
+ }
101
+ const names = new Map();
102
+ for (const [key, components] of paths) {
103
+ const parent = declarations.get(components.slice(0, -1).join("/"));
104
+ const child = declarations.get(key);
105
+ const name = components.at(-1);
106
+ if (parent?.kind !== "directory" || child === undefined || name === undefined) {
107
+ return yield* imageFailure("fromFixture", "InvalidStructure", { field: "parent" });
108
+ }
109
+ names.set(child, [...(names.get(child) ?? []), { parent: parent.ino, name }]);
110
+ }
111
+ const specs = [...new Set(declarations.values())].map((declared) => {
112
+ const links = names.get(declared) ?? [];
113
+ const common = { ino: declared.ino, metadata: declared.metadata, revision: 1n };
114
+ if (declared.kind === "directory") {
115
+ // The root has no path; directories cannot have hard links.
116
+ const [link = { parent: ROOT_INO, name: "" }] = links;
117
+ return { ...common, kind: "directory", parent: link.parent, name: link.name };
118
+ }
119
+ const payload = declared.payload ?? new Uint8Array();
120
+ return declared.kind === "file"
121
+ ? { ...common, kind: "file", links, data: payload }
122
+ : { ...common, kind: "symlink", links, target: payload };
123
+ });
124
+ const restored = VolumeSource.Restored({ value: assemble(specs) });
125
+ const { identity, ...volumeOptions } = config;
126
+ if (identity === undefined)
127
+ return (yield* makeVolume(restored, volumeOptions)).volume;
128
+ const { volume } = yield* makeVolume(restored, {
129
+ ...volumeOptions,
130
+ identity: VolumeIdentity.make(identity)
131
+ });
132
+ return volume;
133
+ });